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);