cfd5fcabf643de689fd383958047718f67fe6575
braney
  Mon Aug 10 11:52:18 2026 -0700
hgTracks: don't split a squishyPack track when the squished part is off screen, refs #37974

expandSquishyPackTracks decided to clone the track from slCount(squishTrack->items)
alone, without asking whether any of those items are in the window.  Items outside
the window are never laid out into a row, so a squink built only from those has no
rows and no height, yet it still claims a center label slice at y == pixHeight and
sliceIsConsistent warns "slice(center) has an invalid X or Y offset".  Only split
when at least one item above the point really overlaps the window, the same test
packCountRowsOverflow uses, with hgFind matches skipped since those always stay in
pack.

That makes the later "if the squish track has no items, continue" check unreachable,
so it goes.  Removing it fixes a second bug: that path skipped both slReverse()
calls, so a squishyPack track whose items all stayed in pack kept its item list
reversed.

The warning goes to stderr and verbose is hIsPrivateHost(), so only hgwdev ever
showed it.  The reversed item list was visible everywhere.

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 692f3e5aa4f..14d988cdee8 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -4987,30 +4987,51 @@
     return;
 
 struct track *nextTrack = NULL, *track;
 for (track = trackList; track != NULL; track = nextTrack)
     {
     nextTrack = track->next;
 
     if ((track->visibility != tvPack) || checkIfWiggling(cart, track))
         continue;
 
     char *string = cartOrTdbString(cart, track->tdb,  "squishyPackPoint", NULL);
     if (string != NULL)
         {
         double squishyPackPoint = atof(string);
 
+        /* Items outside the window are never laid out into a row, so a squished track
+         * built only from those has no rows, no height, and nothing drawn, but still
+         * claims a row in the image.  Only split the track when the squished part has
+         * something to show here. */
+        struct linkedFeatures *item;
+        boolean squishyInWindow = FALSE;
+        for (item = track->items; item != NULL; item = item->next)
+            {
+            if ((hgFindMatches != NULL) && hashLookup(hgFindMatches, item->name))
+                continue;   // an hgFind match always stays in pack
+            if (item->squishyPackVal > squishyPackPoint
+            &&  track->itemStart(track, item) < winEnd
+            &&  track->itemEnd(track, item)   > winStart)
+                {
+                squishyInWindow = TRUE;
+                break;
+                }
+            }
+        if (!squishyInWindow)
+            continue;
+
         /* clone the track */
         char buffer[strlen(track->track) + strlen("Squinked") + 1];
         safef(buffer, sizeof buffer, "%sSquinked", track->track);
 
         struct track *squishTrack = CloneVar(track);
         squishTrack->tdb = CloneVar(track->tdb);
         squishTrack->tdb->originalTrack = squishTrack->tdb->track;
         squishTrack->tdb->track = cloneString(buffer);
         squishTrack->tdb->next = NULL;
         squishTrack->visibility = tvSquish;
         squishTrack->limitedVis = tvSquish;
         hashAdd(trackHash, squishTrack->tdb->track, squishTrack);
         struct linkedFeatures *lf = track->items;
 
         /* distribute the items based on squishyPackPoint */
@@ -5018,34 +5039,30 @@
         squishTrack->items = NULL;
         struct linkedFeatures *nextLf;
         for(; lf; lf = nextLf)
             {
             nextLf = lf->next;
 
             // if this is a hgFind match, it always is in pack, not squish
             if ((hgFindMatches != NULL) && hashLookup(hgFindMatches, lf->name))
                 slAddHead(&track->items, lf);
             else if (lf->squishyPackVal > squishyPackPoint)
                 slAddHead(&squishTrack->items, lf);
             else
                 slAddHead(&track->items, lf);
             }
 
-        // if the squish track has no items, don't bother including it
-        if (slCount(squishTrack->items) == 0)
-            continue;
-
         slReverse(&track->items);
         slReverse(&squishTrack->items);
         
         squishTrack->track = cloneString(buffer);
         squishTrack->originalTrack = cloneString(track->track);
         squishTrack->shortLabel = cloneString(track->shortLabel);
         squishTrack->longLabel = cloneString(track->longLabel);
 
         /* insert the squished track */
         track->next = squishTrack;
         squishTrack->next = nextTrack;
         }
     }
 }