644f1ed70c1b04896944f66bdfa03c16278135d6 braney Fri Jul 31 15:16:12 2026 -0700 quickLift: keep the difference lines from standing still during a drag The difference lines are drawn up over a track's center label, but center labels don't scroll with the data, so the lines sat still while the image moved underneath them. hgTracks now keeps each center label as text with the color it was drawn in, and flags the label when lines were painted over it. While the image is being dragged hgTracks.js hides that slice and puts the label up as text in its place, then swaps the image back on drop. Windows with no difference lines drawn are untouched. refs #37974 diff --git src/hg/hgTracks/quickLift.c src/hg/hgTracks/quickLift.c index 558059f00d0..3d5cf9cd1f2 100644 --- src/hg/hgTracks/quickLift.c +++ src/hg/hgTracks/quickLift.c @@ -1,214 +1,221 @@ #include "common.h" #include "hgTracks.h" #include "chromAlias.h" #include "hgConfig.h" #include "bigChain.h" #include "bigLink.h" #include "trackHub.h" #include "quickLift.h" #include "chainCart.h" #include "bigChain.h" static Color *highlightColors; //static unsigned lengthLimit; struct cartOptions { enum chainColorEnum chainColor; /* ChromColors, ScoreColors, NoColors */ int scoreFilter ; /* filter chains by score if > 0 */ }; static Color getColor(char *confVariable, char *defaultRgba) { Color color = 0xff0000ff; char *str = cloneString(cfgOptionDefault(confVariable, defaultRgba)); char *words[10]; if (chopCommas(str, words) != 4) errAbort("conf variable '%s' is expected to have a string like r,g,b,a ", confVariable); color = MAKECOLOR_32_A(atoi(words[0]),atoi(words[1]),atoi(words[2]),atoi(words[3])); return color; } static void initializeHighlightColors() { if (highlightColors == NULL) { highlightColors = needMem(sizeof(Color) * 5); //lengthLimit = atoi(cfgOptionDefault("quickLift.lengthLimit", "10000")); highlightColors[QUICKTYPE_INSERT] = getColor("quickLift.insColor","255,0,0,255"); highlightColors[QUICKTYPE_DEL] = getColor("quickLift.delColor","0,255,0,255"); highlightColors[QUICKTYPE_DOUBLE] = getColor("quickLift.doubleColor","0,0,255,255"); highlightColors[QUICKTYPE_MISMATCH] = getColor("quickLift.mismatchColor","255,0,0,255"); } } static void drawTri(struct hvGfx *hvg, int x1, int x2, int y, Color color) /* Draw traingle. */ { struct gfxPoly *poly = gfxPolyNew(); int half = (x2 - x1) / 2; gfxPolyAddPoint(poly, x1, y); gfxPolyAddPoint(poly, x1+half, y+2*half); gfxPolyAddPoint(poly, x2, y); hvGfxDrawPoly(hvg, poly, color, TRUE); gfxPolyFree(&poly); } static void mapDenseChain(struct track *tg, struct hvGfx *hvg, char *fileName, int start, int end, int xOff, int yOff, int width, MgFont *font, Color color) // There are no alignment differences in this window, so draw nothing, but still lay down // map boxes over the chains so users can click through to the other species. { struct lm *lm = lmInit(0); struct bbiFile *bbi = bigBedFileOpenAlias(fileName, chromAliasFindAliases); struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chromName, start, end, 0, lm); char *bedRow[12]; char startBuf[16], endBuf[16]; double scale = scaleForWindow(width, start, end); for(bb=bbList; bb; bb = bb->next) { bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow)); struct bigChain *chain = bigChainLoad(bedRow); if (chain->chromStart < start) chain->chromStart = start; if (chain->chromEnd > end) chain->chromEnd = end; int x1 = xOff + scale * (chain->chromStart - start); int w = scale * (chain->chromEnd - chain->chromStart); char otherChrom[1024]; safef(otherChrom, sizeof otherChrom, "%s:%d-%d", chain->qName, chain->qStart, chain->qEnd); mapBoxHc(hvg, chain->chromStart, chain->chromEnd, x1, yOff, w, tg->height, tg->track, chain->name, otherChrom); } } void maybeDrawQuickLiftLines( struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw the indel regions in quickLifted tracks as a highlight. */ { char *quickLiftFile = cloneString(trackDbSetting(tg->tdb, "quickLiftUrl")); if (quickLiftFile == NULL) return; initializeHighlightColors(); boolean drawTriangle = FALSE; if (startsWith("bigQuickLiftChain", tg->tdb->type)) drawTriangle = TRUE; char *liftDb = trackDbSetting(tg->tdb, "quickLiftDb"); struct quickLiftRegions *regions = quickLiftGetRegions(database, liftDb, quickLiftFile, chromName, seqStart, seqEnd); struct quickLiftRegions *hr = regions; int fontHeight = mgFontLineHeight(tl.font); int height = tg->height; if (!drawTriangle && isCenterLabelIncluded(tg)) { height += fontHeight; yOff -= fontHeight; + if ((regions != NULL) && theImgBox && curImgTrack) + { + // There are lines to draw and they go up over the center label. dragScroll + // can't show that slice of the image while the image is moving, so tell it to + // put up the center label as text instead (see panImages() in hgTracks.js). + curImgTrack->cntrLabDrawnOver = TRUE; + } } if (drawTriangle && (hr == NULL)) { mapDenseChain(tg, hvg, quickLiftFile, seqStart, seqEnd, xOff, yOff, width, font, color); return; } int pos = xOff; char * lastId = 0; for(; hr; hr = hr->next) { unsigned int hexColor = highlightColors[hr->type]; double scale = scaleForWindow(width, seqStart, seqEnd); int x1 = xOff + scale * (hr->chromStart - seqStart); int w = scale * (hr->chromEnd - hr->chromStart); if (w == 0) w = 1; hvGfxSetClip(hvg, xOff, yOff, width, height); // we're drawing in the center label at the moment int startX, endX; char mouseOver[4096]; if (drawTriangle) { startX = x1 + w/2 - fontHeight/2; endX = x1 + w/2 + fontHeight/2 ; drawTri(hvg, startX, endX , yOff, hexColor); if (drawTriangle && (x1 > pos)) mapBoxHc(hvg, seqStart, seqEnd, pos, yOff, startX - pos, tg->height, tg->track, hr->id, "identical"); pos = endX; } else { startX = x1; endX = x1 + w; hvGfxBox(hvg, startX, yOff, w, height, hexColor); continue; } if (hr->type == QUICKTYPE_MISMATCH) safef(mouseOver, sizeof mouseOver, "mismatch %c->%c", *hr->otherBases, *hr->bases); else if (hr->chromStart == hr->chromEnd) safef(mouseOver, sizeof mouseOver, "deletion %ldbp (%.*s)", hr->oChromEnd - hr->oChromStart, hr->otherBaseCount, hr->otherBases); else if (hr->oChromStart == hr->oChromEnd) safef(mouseOver, sizeof mouseOver, "insertion %ldbp (%.*s)", hr->chromEnd - hr->chromStart, hr->baseCount, hr->bases); else safef(mouseOver, sizeof mouseOver, "double %ldbp", hr->oChromEnd - hr->oChromStart); // Encode the specific difference in the item name so the details page can // identify which one was clicked: chainId.type.chromStart.chromEnd. The // "identical" boxes above keep the bare chain id. char itemName[256]; safef(itemName, sizeof itemName, "%s.%d.%ld.%ld", hr->id, hr->type, hr->chromStart, hr->chromEnd); mapBoxHc(hvg, seqStart, seqEnd, startX, yOff, endX - startX, height, tg->track, itemName, mouseOver); lastId = hr->id; } if (drawTriangle && (pos != xOff)) mapBoxHc(hvg, seqStart, seqEnd, pos, yOff, width - pos, tg->height, tg->track, lastId, "identical"); } void quickLiftDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) { //mapBoxHc(hvg, seqStart, seqEnd, xOff, yOff, width, tg->height, tg->track, "fart", "hallo"); maybeDrawQuickLiftLines( tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } static int quickLiftTotalHeight(struct track *tg, enum trackVisibility vis) /* Return configured height of track. */ { return tg->lineHeight; } static void quickLiftDoLeftLabels(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, int height, boolean withCenterLabels, MgFont *font, Color color, enum trackVisibility vis) /* For now, draw no left labels. */ { return; } void quickLiftChainMethods(struct track *tg, struct trackDb *tdb, int wordCount, char *words[]) /* Fill in custom parts of quickLift chains */ { struct cartOptions *chainCart; AllocVar(chainCart); tg->extraUiData = (void *) chainCart; tg->isBigBed = TRUE; void bigChainLoadItems(struct track *tg); tg->loadItems = bigChainLoadItems; tg->drawItems = quickLiftDraw; tg->totalHeight = quickLiftTotalHeight; tg->drawLeftLabels = quickLiftDoLeftLabels; tg->mapsSelf = TRUE; }