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; } } }