57aba7b8fb23d9bdbeceb665a001452e6d7a6b99 jcasper Thu Jan 8 00:37:48 2026 -0800 An attempt at better auto-scaling for Hi-C colors in arc mode, refs #36774 diff --git src/hg/hgTracks/hicTrack.c src/hg/hgTracks/hicTrack.c index 789abd0cc38..dc5d82d7fd0 100644 --- src/hg/hgTracks/hicTrack.c +++ src/hg/hgTracks/hicTrack.c @@ -85,80 +85,82 @@ /* Load all Hi-C items in the current region and identify the window height * and median value for this region. */ { if (tg->customPt == NULL) tg->customPt = grabHeader(tg); if (tg->customPt == NULL) return; struct hicMeta *hicFileInfo = (struct hicMeta*)tg->customPt; int binSize = hicUiFetchResolutionAsInt(cart, tg->tdb, hicFileInfo, winEnd-winStart); char *normalization = hicUiFetchNormalization(cart, tg->tdb, hicFileInfo); char abbrevBinSize[1024]; sprintWithMetricBaseUnit(abbrevBinSize, sizeof(abbrevBinSize), binSize); int newStringLen = strlen(tg->longLabel) + strlen(abbrevBinSize) + strlen(normalization) + 10; +char *drawMode = hicUiFetchDrawMode(cart, tg->tdb); char *newLabel = needMem(newStringLen); safef(newLabel, newStringLen, "%s (%s, %s)", tg->longLabel, abbrevBinSize, normalization); tg->longLabel = newLabel; // leaks old cloneString() memory chunk // Later, it would be nice to validate that this file is for the current assembly (see the hicMeta // structure). It would be hard - the assembly name in the file's "genome" field can't be relied on. // Maybe by comparing chromosome names and sizes? // Note: This is giving it a 0-based, full-closed window. Straw seems to use 1-based coordinates // by default, but accepts 0 as the start of a window without complaint. // Pad the start because we want to display partial interactions if the end of an interacting block is // in view but not the start. Straw won't report interactions if the start of the block isn't in the // supplied position range. int strawStart = winStart - binSize + 1; if (strawStart < 0) strawStart = 0; struct interact *hicItems = NULL; tg->networkErrMsg = hicLoadData(hicFileInfo, binSize, normalization, chromName, strawStart, winEnd-1, chromName, strawStart, winEnd-1, &hicItems); // Using the interact structure because it has convenient fields, but this is not interact data and // shouldn't be passed to those functions. int numRecords = slCount(hicItems), filtNumRecords = 0; tg->maxRange = 0.0; // the max height of an interaction in this window double *countsCopy = NULL; if (numRecords > 0) AllocArray(countsCopy, numRecords); struct interact *thisHic = hicItems; -char *drawMode = hicUiFetchDrawMode(cart, tg->tdb); struct interact* filteredOut = NULL; struct interact** prevNextPtr = &hicItems; // for removing items from the linked list double maxRange = hicUiMaxInteractionRange(cart, tg->tdb); double minRange = hicUiMinInteractionRange(cart, tg->tdb); +int filteredOutCount = 0; // For reporting how many elements were excluded (not including self loops) while (thisHic != NULL) { // Add filtering based on max interaction distance if (sameString(thisHic->sourceChrom, thisHic->targetChrom)) { unsigned leftEdge = thisHic->sourceStart < thisHic->targetStart ? thisHic->sourceStart : thisHic->targetStart; unsigned rightEdge = thisHic->sourceEnd > thisHic->targetEnd ? thisHic->sourceEnd : thisHic->targetEnd; if ((maxRange && maxRange < (double)(rightEdge - leftEdge) ) || (minRange && minRange > (double)(rightEdge - leftEdge) )) { // a bit of pointer play to avoid repeated calls to slRemoveEl *prevNextPtr = thisHic->next; // set prev element's next to the following element slAddHead(&filteredOut, thisHic); thisHic = *prevNextPtr; // restore thisHic to point to the next element + filteredOutCount++; continue; } } if (sameString(drawMode, HIC_DRAW_MODE_ARC)) { // we omit self-interactions in arc mode (they'd just be weird vertical lines) if (sameString(thisHic->sourceChrom, thisHic->targetChrom) && (thisHic->sourceStart == thisHic->targetStart)) { // a bit of pointer play to avoid repeated calls to slRemoveEl *prevNextPtr = thisHic->next; // set prev element's next to the following element slAddHead(&filteredOut, thisHic); thisHic = *prevNextPtr; // restore thisHic to point to the next element continue; @@ -173,37 +175,65 @@ if (sameString(drawMode,HIC_DRAW_MODE_SQUARE)) thisHeight = scaleForWindow(insideWidth, winStart, winEnd)*(winEnd-winStart); // square - always draw the full square if (thisHeight > tg->maxRange) tg->maxRange = thisHeight; prevNextPtr = &thisHic->next; thisHic = thisHic->next; } if (filteredOut != NULL) interactFreeList(&filteredOut); // Heuristic for auto-scaling the color gradient based on the scores in view - draw the max color value // at or above 2*median score. if (filtNumRecords > 0) - tg->graphUpperLimit = 2.0*doubleMedian(filtNumRecords, countsCopy); + { + double median = doubleMedian(filtNumRecords, countsCopy); + tg->graphUpperLimit = 2.0*median; + //if (sameString(drawMode, HIC_DRAW_MODE_ARC) && filtNumRecords > 1000) + if (filtNumRecords > 1000) + { + int ix = (int)(filtNumRecords/(0.000008*filtNumRecords + 6.462459)); + if (ix < 1) + ix = 1; + tg->graphUpperLimit = countsCopy[filtNumRecords-ix]; + } + } else tg->graphUpperLimit = 0.0; if (countsCopy != NULL) freeMem(countsCopy); tg->items = hicItems; + +int hiddenCount = filteredOutCount; +if (sameString(drawMode, HIC_DRAW_MODE_ARC) && hicUiArcLimitEnabled(cart, tg->tdb)) + { + int itemLimit = hicUiGetArcLimit(cart, tg->tdb); + if (filtNumRecords > itemLimit) + hiddenCount += (filtNumRecords-itemLimit); + } +if (hiddenCount > 0) + { + char filterString[1024] = ""; + safef(filterString, sizeof(filterString), " (%d items filtered out)", hiddenCount); + newLabel = catTwoStrings(tg->longLabel, filterString); + freeMem(tg->longLabel); + tg->longLabel = newLabel; + } + } void hicLoadItems(struct track *tg) /* Load Hi-C items in (mostly) interact format */ { char *filename = trackDbSettingOrDefault(tg->tdb, "bigDataUrl", NULL); if (filename == NULL) return; if (tg->customPt == NULL) { tg->customPt = grabHeader(tg); struct track *hicInNextWindow = tg->nextWindow; while (hicInNextWindow != NULL) { // pre-cache the hic header info; no reason to re-fetch