34f380bfb0b4003a23fe2e00be6bdac6af326cda
jcasper
  Mon Dec 15 15:20:13 2025 -0800
Adding hicArcLimit and hicArcLimitEnabled settings to let people prevent excessive
arc counts from overwhelming a Hi-C display with data, refs #36774

diff --git src/hg/hgTracks/hicTrack.c src/hg/hgTracks/hicTrack.c
index ec4053ddb49..789abd0cc38 100644
--- src/hg/hgTracks/hicTrack.c
+++ src/hg/hgTracks/hicTrack.c
@@ -145,58 +145,62 @@
             {
             // 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;
             }
         }
 
     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))
             {
-            thisHic = thisHic->next;
+            // 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
     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);
 else
     tg->graphUpperLimit = 0.0;
+
 if (countsCopy != NULL)
     freeMem(countsCopy);
 tg->items = hicItems;
 }
 
 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;
@@ -425,54 +429,68 @@
 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
-for (hicItem = (struct interact *)tg->items; hicItem; hicItem = hicItem->next)
+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)
     {
     int leftStart = hicItem->sourceStart - winStart;
     int leftMidpoint = leftStart + binSize/2;
     int rightStart = hicItem->targetStart - winStart;
     int rightMidpoint = rightStart + binSize/2;
+
     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
     if (!invert)
         midy = maxHeight-(int)midy;
-    midy *= 1.5; // Heuristic scaling for better use of vertical space
     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. */
 {
 char *drawMode = hicUiFetchDrawMode(cart, tg->tdb);
 if (sameString(drawMode,HIC_DRAW_MODE_SQUARE))