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;