1e04d78b65b35678e80d9f5929e6fec0f7b2fbbb
hiram
  Tue Mar 24 13:32:54 2026 -0700
refactor the GC on fly calculation to move into a library function rfefs #35958

diff --git src/hg/hgTracks/bigWigTrack.c src/hg/hgTracks/bigWigTrack.c
index 6898c051dd1..4f793292e2e 100644
--- src/hg/hgTracks/bigWigTrack.c
+++ src/hg/hgTracks/bigWigTrack.c
@@ -360,86 +360,69 @@
  * bigWig 0 100 range.  wigTrack.c windowing/smoothing handles averaging
  * when multiple windows fall on the same pixel.
  */
 {
 char *winSizeString = trackDbSetting(tg->tdb, "calcWinSize");
 int winSize = (int)sqlLongLong(winSizeString);
 
 if (tg->preDrawContainer)
     return tg->preDrawContainer;
 
 struct preDrawContainer *pre = tg->preDrawContainer = initPreDrawContainer(width);
 struct preDrawElement *preDraw = pre->preDraw;
 int preDrawZero = pre->preDrawZero;
 int preDrawSize = pre->preDrawSize;
 
-struct dnaSeq *seq = hChromSeq(database, chromName, seqStart, seqEnd);
-if (seq == NULL)
+struct gcOnTheFlyWindow *windows = NULL;
+int windowCount = gcOnTheFlyCompute(database, chromName, seqStart, seqEnd,
+    winSize, &windows);
+if (windowCount == 0)
     return pre;
 
-int seqLen = seqEnd - seqStart;
 /* pixelsPerBase is based on the visible window, not the extended fetch range,
  * so that pixel coordinates are correct relative to the display. */
 double pixelsPerBase = (double)width / (winEnd - winStart);
 int span = winSize;
-int pos;
+int wi;
 
-/* Align to the winSize-base chromosome boundary, not the window boundary.
- * startOffset is the number of bases into the fetched sequence where
- * the first chromosome-aligned winSize-base window begins. */
-int startOffset = (span - (seqStart % span)) % span;
-
-for (pos = startOffset; pos + span <= seqLen; pos += span)
-    {
-    int gcCount = 0, validBases = 0;
-    int i;
-    for (i = pos; i < pos + span; i++)
+for (wi = 0; wi < windowCount; wi++)
     {
-        char b = seq->dna[i];
-        if (b == 'g' || b == 'c')  { gcCount++;  validBases++; }
-        else if (b != 'n')           validBases++;
-        }
-    if (validBases == 0)
-        continue;
-
-    double gcPct = 100.0 * gcCount / validBases;   /* 0 - 100 */
+    double gcPct = windows[wi].gcPct;
+    int chromPos = windows[wi].chromStart;
 
     /* Map this span to pixel coordinates relative to winStart.
      * Data outside the visible window lands in the preDraw margin slots
      * (negative or >= width), which is correct for smoothing at edges.
-     * Fill every pixel covered by this 5-base span. */
-    int chromPos = seqStart + pos;
+     * Fill every pixel covered by this span. */
     int x1 = (int)((chromPos - winStart) * pixelsPerBase);
     int x2 = (int)((chromPos + span - winStart) * pixelsPerBase);
     int xi;
     for (xi = x1; xi <= x2; ++xi)
         {
         int xCoord = preDrawZero + xi;
         if (xCoord >= 0 && xCoord < preDrawSize)
             {
             preDraw[xCoord].count++;
             if (gcPct > preDraw[xCoord].max) preDraw[xCoord].max = gcPct;
             if (gcPct < preDraw[xCoord].min) preDraw[xCoord].min = gcPct;
             preDraw[xCoord].sumData    += gcPct;
             preDraw[xCoord].sumSquares += gcPct * gcPct;
             }
         }
     }
 
-dnaSeqFree(&seq);
-// if (measureTiming)
-//     measureTime("GC5 on the fly calculation");
+freeMem(windows);
 return pre;
 }
 
 static void gc5BaseOnTheFlyLoadItems(struct track *tg)
 /* Compute GC percent from genome sequence; called in the loadItems phase
  * just as bigWigLoadItems calls bigWigLoadPreDraw to fill preDrawContainer.
  * Fetch sequence that covers the preDraw smoothing margins on each side so
  * the smoothing/averaging machinery has data at the window edges. */
 {
 tg->items = NULL;
 tg->mapsSelf = TRUE;
 /* Extend the fetch range by wiggleSmoothingMax pixels worth of bases on
  * each side, rounded up to the nearest 5-base span. */
 double basesPerPixel = (double)(winEnd - winStart) / insideWidth;
 int marginBases = ((int)(wiggleSmoothingMax * basesPerPixel) / 5 + 1) * 5;