6dfb86f7f9c25c2fd3b332695b9e79dc0c5184ee chmalee Fri Feb 25 16:06:50 2022 -0800 Fix 3 barChart issues: One allow users to define a bar width and padding, two fix bar drawing bug where dynamically sizing the bars based on zoom level was causing some bars to be drawn at inconsistent widths in the same chart, three fix barChartStretchToItem bug when zoomed in to the base level such that your window size is less than the item size, refs #28977 diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c index 6fcea00..9d7765d 100644 --- src/hg/hgTracks/barChartTrack.c +++ src/hg/hgTracks/barChartTrack.c @@ -377,31 +377,36 @@ return height; } static int chartStandardWidth(struct track *tg, struct barChartItem *itemInfo) /* How wide is chart if we go by standard bar chart size? */ { struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; int count = filteredCategoryCount(extras); return (extras->barWidth * count) + (extras->padding * (count-1)) + 2; } static int windowsTotalIntersection(struct window *list, char *chrom, int chromStart, int chromEnd) /* Return total size all bits of region defined by chrom/start/end that intersects windows list */ { if (list == NULL || list->next == NULL) - return (double)insideWidth * (chromEnd - chromStart) / (winEnd - winStart); + { + double scale = scaleForWindow(insideWidth, winStart, winEnd); + int leftBlank = round(scale * (max(winStart, chromStart) - winStart)); + int rightBlank = round(scale * (winEnd - min(winEnd, chromEnd))); + return (double)insideWidth - leftBlank - rightBlank; + } long long totalGenoSize = 0; int totalPixelSize = 0; struct window *w; int totalIntersect = 0; for (w = list; w != NULL; w = w->next) { totalPixelSize += w->insideWidth;; totalGenoSize += w->winEnd - w->winStart; if (sameString(w->chromName, chrom)) { int single = rangeIntersection(w->winStart, w->winEnd, chromStart, chromEnd); if (single > 0) totalIntersect += single; } @@ -536,30 +541,34 @@ filterCategories(tg); /* Test that configuration matches data file */ if (bedList != NULL) { int categCount = getCategoryCount(tg); int expCount = bedList->expCount; if (categCount != expCount) warn("Bar chart track: category count mismatch between trackDb (%d) and data file (%d)", categCount, expCount); } int barCount = filteredCategoryCount(extras); +// users can set minimum bar width and padding in trackDb +char *userBarWidth = trackDbSetting(tdb, BAR_CHART_USER_BAR_WIDTH); +char *userBarPadding = trackDbSetting(tdb, BAR_CHART_USER_BAR_PADDING); + /* Scaling here is pretty ad-hoc! */ double scale = 1.0; if (barCount <= 20) scale = 2.5; else if (barCount <= 40) scale = 1.6; else if (barCount <= 60) scale = 1.0; else scale = 60.0/barCount; #ifdef OLD else if (barCount <= 120) scale = 0.8; else if (barCount <= 200) scale = 0.6; @@ -608,33 +617,46 @@ { extras->boxModelHeight = MIN_BAR_CHART_MODEL_HEIGHT; extras->barWidth = MIN_BAR_WIDTH * scale; extras->padding = MIN_GRAPH_PADDING * scale; extras->maxHeight = tl.fontHeight * 4; } if (extras->barWidth > 1) extras->barWidth = floor(extras->barWidth); if (extras->barWidth <= 1 && extras->padding == 1) { extras->barWidth = 2; extras->padding = 0; } if (extras->barWidth < 1) + { + extras->barWidth = 1; extras->padding = 0; + } else extras->barWidth = round(extras->barWidth); +if (userBarWidth) + { + int barWidth = sqlUnsigned(userBarWidth); + extras->barWidth = barWidth > extras->barWidth ? barWidth : extras->barWidth; + } +if (userBarPadding) + { + int barPadding = sqlUnsigned(userBarPadding); + extras->padding = barPadding > extras->padding ? barPadding : extras->padding; + } extras->modelHeight = extras->boxModelHeight + 3; extras->margin = 1; extras->squishHeight = tl.fontHeight - tl.fontHeight/2; extras->stretchToItem = trackDbSettingOn(tg->tdb, "barChartStretchToItem"); struct bed *bed, *next; for (bed = bedList; bed != NULL; bed = next) { next = bed->next; bed->next = NULL; /* Pop it off list */ AllocVar(itemInfo); itemInfo->bed = (struct bed *)bed; slAddHead(&list, itemInfo); itemInfo->height = barChartItemHeight(tg, itemInfo); @@ -740,47 +762,51 @@ 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; 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; +char *userBarWidth = trackDbSetting(tg->tdb, BAR_CHART_USER_BAR_WIDTH); +if ((extras->padding == 0 || sameString(colorScheme, BAR_CHART_COLORS_USER)) + && !userBarWidth) + { + // scale the barWidth on the graph size at this zoom level + barWidth = max(barWidth, graphWidth * invCount); + } for (i=0, categ=extras->categories; inext) { if (!filterCategory(extras, categ->name)) continue; 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)) { - int cStart = barsDrawn * graphWidth * invCount; - int cEnd = (barsDrawn+1) * graphWidth * invCount; - x1 = cStart + x0; - barWidth = cEnd - cStart - extras->padding; + x1 = x0 + (barWidth * barsDrawn); hvGfxBox(hvg, x1, barTop, barWidth, height, fillColorIx); if (isClipped) hvGfxBox(hvg, x1, barTop, barWidth, 2, clipColor); barsDrawn += 1; } 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 = x1 + barWidth + extras->padding; } } }