2f6c858cd7d9b2fc0b48f8618bef5e74712c6639 jcasper Tue Feb 3 23:16:15 2026 -0800 Hi-C track height in arc mode should match the height of the highest arc, refs #36774 diff --git src/hg/hgTracks/hicTrack.c src/hg/hgTracks/hicTrack.c index dc5d82d7fd0..2f0a76895a7 100644 --- src/hg/hgTracks/hicTrack.c +++ src/hg/hgTracks/hicTrack.c @@ -69,30 +69,42 @@ struct hicMeta *metaResult = NULL; if (filename == NULL) { warn("Missing bigDataUrl setting for track %s", tg->track); return NULL; } char *errMsg = hicLoadHeader(filename, &metaResult, database); if (errMsg != NULL) { tg->networkErrMsg = errMsg; return NULL; } return metaResult; } +int cmpHicItems(const void *elem1, const void *elem2) +/* Comparison function for sorting Hi-C interactions by score */ +{ +struct interact *item1 = *((struct interact**)elem1); +struct interact *item2 = *((struct interact**)elem2); +if (item1->value > item2->value) + return 1; +if (item1->value < item2->value) + return -1; +return 0; +} + static void loadAndFilterItems(struct track *tg) /* 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); @@ -159,50 +171,50 @@ 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; } } countsCopy[filtNumRecords++] = thisHic->value; // Calculate the track draw height required to see this item int leftx = max(thisHic->chromStart, winStart); int rightx = min(thisHic->chromEnd, winEnd); - double thisHeight = scaleForWindow(insideWidth, winStart, winEnd)*(rightx - leftx)/2.0; // triangle or arc + // Height in triangle or square mode (we handle arcs separately a bit later) + double thisHeight = scaleForWindow(insideWidth, winStart, winEnd)*(rightx - leftx)/2.0; // triangle 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) { 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; @@ -210,30 +222,59 @@ 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; } +if (sameString(drawMode, HIC_DRAW_MODE_ARC)) + { + // Handle track height calculations in arc mode (have to sort the arcs first) + slSort(&tg->items, cmpHicItems); // So that the darkest arcs are drawn on top (i.e. last) and not lost + struct interact *this = (struct interact*) tg->items; + if (hicUiArcLimitEnabled(cart, tg->tdb)) + { + // Skip past the arcs that won't be drawn + int limit = hicUiGetArcLimit(cart, tg->tdb); + int itemCount = filtNumRecords; + while (itemCount > limit && this != NULL) + { + this = this->next; + itemCount--; + } + } + tg->maxRange = 0; + while (this != NULL) + { + int leftx = max(this->chromStart, winStart); + int rightx = min(this->chromEnd, winEnd); + // The max height of this Bezier will be half the height of the middle control point. We're putting + // the control point height at 0.75 of the width between the endpoints because it looks decent. + double thisHeight = 0.75 * scaleForWindow(insideWidth, winStart, winEnd)*(rightx - leftx)/2.0; + if (thisHeight > tg->maxRange) + tg->maxRange = thisHeight; + this = this->next; + } + } } 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 @@ -424,65 +465,52 @@ y = maxHeight-(int)y; if (invert) y = yScale * ((winEnd-winStart)-leftEnd); hvGfxBox(hvg, (int)x+xOff, (int)y+yOff, (int)(xScale*(rightEnd-rightStart))+1, (int)(yScale*(leftEnd-leftStart))+1, colorIx); } } // Draw top-left to bottom-right diagonal axis line in black int colorIx = hvGfxFindColorIx(hvg, 0, 0, 0); if (invert) // Draw bottom-left to top-right instead hvGfxLine(hvg, xOff, maxHeight+yOff, ((winEnd-winStart)*xScale)+xOff, yOff, colorIx); else hvGfxLine(hvg, xOff, yOff, ((winEnd-winStart)*xScale)+xOff, maxHeight+yOff, colorIx); } -int cmpHicItems(const void *elem1, const void *elem2) -/* Comparison function for sorting Hi-C interactions by score */ -{ -struct interact *item1 = *((struct interact**)elem1); -struct interact *item2 = *((struct interact**)elem2); -if (item1->value > item2->value) - return 1; -if (item1->value < item2->value) - return -1; -return 0; -} - static void drawHicArc(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw Hi-C interactions in arc mode */ { double xScale = scaleForWindow(width, seqStart, seqEnd); double yScale = xScale; int maxHeight = tg->height; struct interact *hicItem = NULL; struct hicMeta *hicFileInfo = (struct hicMeta*)tg->customPt; int binSize = hicUiFetchResolutionAsInt(cart, tg->tdb, hicFileInfo, winEnd-winStart); boolean invert = hicUiFetchInverted(cart, tg->tdb); if (binSize == 0) return; yScale *= hicSqueezeFactor(vis); double maxScore = getHicMaxScore(tg); Color *colorIxs = colorSetForHic(hvg, tg, HIC_SCORE_BINS+1); if (colorIxs == NULL) return; // something went wrong with colors -slSort(&tg->items, cmpHicItems); // So that the darkest arcs are drawn on top and not lost hicItem = (struct interact *)tg->items; if (hicUiArcLimitEnabled(cart,tg->tdb) && (hicUiGetArcLimit(cart, tg->tdb) > 0)) { // limit to only the X highest scoring interactions int itemCount = slCount(tg->items); int limit = hicUiGetArcLimit(cart, tg->tdb); while (itemCount > limit && hicItem != NULL) { hicItem = hicItem->next; itemCount--; } } for (; hicItem; hicItem = hicItem->next) { @@ -493,32 +521,31 @@ if ((leftMidpoint < 0) || (leftMidpoint > winEnd-winStart)) continue; // skip this item - we'd be drawing to a point off the screen if ((rightMidpoint < 0) || (rightMidpoint > winEnd-winStart)) continue; // skip this item - we'd be drawing to a point off the screen int colorIx; if (hicItem->value > maxScore) colorIx = colorIxs[HIC_SCORE_BINS]; else colorIx = colorIxs[(int)(HIC_SCORE_BINS * hicItem->value/maxScore)]; double leftx = xScale * leftMidpoint; double rightx = xScale * rightMidpoint; double midx = xScale * (rightMidpoint+leftMidpoint)/2.0; - double midy = yScale * (rightMidpoint-leftMidpoint)/2.0; - midy *= 1.5; // Heuristic scaling for better use of vertical space + double midy = yScale * (rightMidpoint-leftMidpoint) * 0.75; // Heuristic scaling for better-looking arcs if (!invert) midy = maxHeight-(int)midy; int lefty = maxHeight, righty = maxHeight; // the height of the endpoints if (invert) lefty = righty = 0; hvGfxCurve(hvg, (int)leftx+xOff, lefty+yOff, (int)midx+xOff, midy+yOff, (int)rightx+xOff, righty+yOff, colorIx, FALSE); } } void hicDrawItems(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw a set of Hi-C interactions with the current user settings. */ {