4a6eac658a9442df276cfeedde11a0bc299af4ae kate Wed Aug 29 15:43:08 2018 -0700 Refactor in preparation for flipped display. refs #21917 diff --git src/hg/hgTracks/interactTrack.c src/hg/hgTracks/interactTrack.c index df88b5b..4c1e0ed 100644 --- src/hg/hgTracks/interactTrack.c +++ src/hg/hgTracks/interactTrack.c @@ -170,47 +170,49 @@ interactRegionCenters(inter, &sourceCenter, &targetCenter); return abs(targetCenter - sourceCenter); } int getX(int pos, int seqStart, double scale, int xOff) /* Get x coordinate of a genomic location. Return -1 if off-screen */ { if (pos < seqStart) return -1; return ((double)(pos - seqStart + .5) * scale) + xOff; } struct interactTrackInfo { boolean isDirectional; // source and target are distinct item types char *offset; // which end to draw offset (source or target) + boolean drawUp; // draw interactions with peak up (hill) boolean doOtherLabels; // true to suppress labels on other chrom items (prevent overlap) int maxSize; // longest interaction (midpoint to midpoint) in bp int fontHeight; int sameCount; // number of same chromosome interactions in window int sameHeight; // vertical space for same chromosome interactions int otherCount; // number of other chromosome interactions in window int otherHeight; // vertical space for other chromosome interactions } interactTrackInfo; struct interactTrackInfo *interactGetTrackInfo(struct track *tg, int seqStart, struct hvGfx *hvg, int xOff, MgFont *font, double scale) /* Get layout info from interact items in window */ { struct interactTrackInfo *tInfo = NULL; AllocVar(tInfo); tInfo->doOtherLabels = TRUE; tInfo->isDirectional = interactUiDirectional(tg->tdb); tInfo->offset = interactUiOffset(tg->tdb); +tInfo->drawUp = trackDbSettingClosestToHomeOn(tg->tdb, INTERACT_UP); char *otherChrom = NULL; int prevLabelEnd = 0, prevLabelStart = 0; char *prevLabel = 0; struct interact *inter; for (inter = (struct interact *)tg->items; inter; inter = inter->next) { otherChrom = interactOtherChrom(inter); if (otherChrom == NULL) { tInfo->sameCount++; // determine maximum interaction size, for later use laying out 'peaks' int size = interactSize(inter); if (size > tInfo->maxSize) @@ -239,67 +241,118 @@ tInfo->otherHeight = (tInfo->otherCount) ? 3 * tInfo->fontHeight : 0; tInfo->sameHeight = (tInfo->sameCount) ? tg->height - tInfo->otherHeight : 0; return tInfo; } char *endpointClickArg(char *endpointName) /* Add var to identify endpoint ('foot'), or NULL if no name for endpoint */ { if (isEmptyTextField(endpointName)) return NULL; char buf[256]; safef(buf, sizeof(buf),"foot=%s", cgiEncode(endpointName)); return(cloneString(buf)); } -static void interactDrawItems(struct track *tg, int seqStart, int seqEnd, +void drawFoot(struct hvGfx *hvg, char *seq, int seqStart, int seqEnd, + int x, int y, int width, Color color, struct hash *footHash) +/* Draw interaction end, 2 pixels high. Force to black if it exactly overlaps another */ +{ +char buf[256]; +safef(buf, sizeof(buf), "%s:%d-%d", seq, seqStart, seqEnd); +char *pos = cloneString(buf); +Color footColor = color; +if (hashLookup(footHash, pos)) + footColor = MG_BLACK; +else + hashStore(footHash, pos); +hvGfxBox(hvg, x, y, width, 2, footColor); +} + +void drawLine(struct hvGfx *hvg, int x1, int y1, int x2, int y2, Color color, boolean isDashed) +/* Draw vertical or horizontal */ +{ +if (isDashed) + hvGfxDottedLine(hvg, x1, y1, x2, y2, color, TRUE); +else + hvGfxLine(hvg, x1, y1, x2, y2, color); +} + +void drawFootMapbox(struct hvGfx *hvg, char *track, + int start, int end, char *item, char *status, + int x, int y, int width, Color peakColor, Color highlightColor) +/* Draw grab box and add map box */ +{ +char *clickArg = endpointClickArg(item); +char *itemBuf = isEmptyTextField(item) ? status : item; +hvGfxBox(hvg, x-1, y, 3, 2, peakColor); +hvGfxBox(hvg, x, y, 1, 1, highlightColor); +mapBoxHgcOrHgGene(hvg, start, end, x - width, y, width * 2, 4, + track, item, itemBuf, NULL, TRUE, clickArg); +} + +void drawPeakMapbox(struct hvGfx *hvg, char *track, + int seqStart, int seqEnd, char *item, char *status, + int x, int y, Color peakColor, Color highlightColor) +/* Draw grab box and add map box */ +{ +hvGfxBox(hvg, x-1, y-1, 3, 3, peakColor); +hvGfxBox(hvg, x, y, 1, 1, highlightColor); +mapBoxHgcOrHgGene(hvg, seqStart, seqEnd, x-1, y-1, 3, 3, + track, item, status, NULL, TRUE, NULL); +} + +void interactDrawItems(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 list of interact structures. */ { #define DRAW_LINE 0 #define DRAW_CURVE 1 #define DRAW_ELLIPSE 2 // Determine drawing mode int draw = DRAW_LINE; +boolean doDashes = FALSE; if (vis != tvDense) { char *drawMode = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, INTERACT_DRAW, INTERACT_DRAW_DEFAULT); if (sameString(drawMode, INTERACT_DRAW_CURVE)) draw = DRAW_CURVE; else if (sameString(drawMode, INTERACT_DRAW_ELLIPSE)) draw = DRAW_ELLIPSE; +doDashes = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE, + INTERACT_DIRECTION_DASHES, INTERACT_DIRECTION_DASHES_DEFAULT); } - double scale = scaleForWindow(width, seqStart, seqEnd); struct interact *inter = NULL; char buffer[1024]; char itemBuf[2048]; int chromStart, chromEnd; // Gather info for layout struct interactTrackInfo *tInfo = interactGetTrackInfo(tg, seqStart, hvg, xOff, font, scale); +int highlightColor = MG_WHITE; // Get spectrum range int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tg->tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tg->tdb, "scoreMax", "1000")); // Draw items -struct hash *footHash = hashNew(0); -char footBuf[256]; +struct hash *footHash = hashNew(0); // track feet so we can override color to black for overlapping +struct hash *footHashOther = hashNew(0); // has for items on other chrom for (inter = (struct interact *)tg->items; inter; inter = inter->next) { char *otherChrom = interactOtherChrom(inter); safef(itemBuf, sizeof itemBuf, "%s", inter->name); char *statusBuf = interactMouseover(inter, otherChrom); // Pick colors #define MG_LIGHT_MAGENTA 0xffffbbff #define MG_LIGHT_GRAY 0xff909090 color = interactItemColor(tg, inter, hvg, scoreMin, scoreMax); if (vis == tvDense && otherChrom && color == MG_BLACK) // use highlight color for other chrom items in dense mode color = MG_LIGHT_MAGENTA; int peakColor = (color == MG_BLACK || tg->colorShades) ? MG_LIGHT_MAGENTA : MG_LIGHT_GRAY; @@ -314,68 +367,62 @@ if (vis == tvDense) { height = tg->height; } else { height = tInfo->otherHeight/2; yOffOther = yOff + tInfo->sameHeight; } unsigned r = interactRegionCenter(inter->chromStart, inter->chromEnd); int x = getX(r, seqStart, scale, xOff); int footWidth = regionFootWidth(inter->chromStart, inter->chromEnd, scale); unsigned yPos = yOffOther + height; // draw the foot (2 pixels high) - hvGfxBox(hvg, x - footWidth, yOffOther, footWidth + footWidth + 1, 2, color); + drawFoot(hvg, inter->chrom, inter->chromStart, inter->chromEnd, + x - footWidth, yOffOther, footWidth + footWidth + 1, color, footHashOther); // draw the vertical - if (tInfo->isDirectional && differentString(inter->chrom, inter->sourceChrom)) - hvGfxDottedLine(hvg, x, yOffOther, x, yPos, color, TRUE); - else - hvGfxLine(hvg, x, yOffOther, x, yPos, color); + boolean isReversed = tInfo->isDirectional && differentString(inter->chrom, inter->sourceChrom); + drawLine(hvg, x, yOffOther, x, yPos, color, isReversed && doDashes); if (vis == tvDense) continue; // add map box to foot + char *nameBuf = (inter->chromStart == inter->sourceStart ? inter->sourceName : inter->targetName); - char *clickArg = endpointClickArg(nameBuf); - if (isEmptyTextField(nameBuf)) - nameBuf = statusBuf; - chromStart = inter->chromStart; - chromEnd = inter->chromEnd; - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, - x - footWidth, yOffOther, footWidth * 2, 4, - tg->track, itemBuf, nameBuf, NULL, TRUE, clickArg); + drawFootMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, nameBuf, statusBuf, + x - footWidth, yOffOther, footWidth, peakColor, highlightColor); // add map box to vertical mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, x - 2, yOffOther, 4, height, tg->track, itemBuf, statusBuf, NULL, TRUE, NULL); if (tInfo->doOtherLabels) { // draw label safef(buffer, sizeof buffer, "%s", sameString(inter->chrom, inter->sourceChrom) ? inter->targetChrom : inter->sourceChrom); hvGfxTextCentered(hvg, x, yPos + 2, 4, 4, MG_BLUE, font, buffer); int labelWidth = vgGetFontStringWidth(hvg->vg, font, buffer); // add map box to label mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, x - labelWidth/2, yPos, labelWidth, tInfo->fontHeight, tg->track, itemBuf, statusBuf, - NULL, TRUE, clickArg); + NULL, TRUE, NULL); } continue; } // Draw same chromosome interaction // source region unsigned s = interactRegionCenter(inter->sourceStart, inter->sourceEnd); int sX = getX(s, seqStart, scale, xOff); int sWidth = regionFootWidth(inter->sourceStart, inter->sourceEnd, scale); boolean sOnScreen = (s >= seqStart) && (s< seqEnd); // target region unsigned t = interactRegionCenter(inter->targetStart, inter->targetEnd); int tX = getX(t, seqStart, scale, xOff); @@ -391,172 +438,122 @@ // NOTE: until time permits, force to rectangle when in reversed strand mode. int yTarget = yOff; int ySource = yOff; if (tInfo->offset && draw != DRAW_ELLIPSE) // ellipse code doesn't support assymetrical ends { int yOffset = tg->height/10 + 1; if (sameString(tInfo->offset, INTERACT_OFFSET_TARGET)) yTarget = yOff + yOffset; else if (sameString(tInfo->offset, INTERACT_OFFSET_SOURCE)) ySource = yOff + yOffset; } unsigned footColor = color; + if (sOnScreen) { - safef(footBuf, sizeof(footBuf), "%s:%d-%d", inter->sourceChrom, - inter->sourceStart, inter->sourceEnd); - char *footPos = cloneString(footBuf); - if (hashLookup(footHash, footPos)) - footColor = MG_BLACK; - else - hashStore(footHash, footPos); - - // draw foot of source region (2 pixels high) - hvGfxBox(hvg, sX - sWidth, ySource, sWidth + sWidth + 1, 2, footColor); + drawFoot(hvg, inter->sourceChrom, inter->sourceStart, inter->sourceEnd, + sX - sWidth, ySource, sWidth + sWidth + 1, footColor, footHash); if (vis == tvDense || !tOnScreen || draw == DRAW_LINE || hvg->rc) { - // draw vertical - if (isReversed && (!tInfo->offset || draw == DRAW_ELLIPSE)) - hvGfxDottedLine(hvg, sX, ySource, sX, peak, color, TRUE); - else - hvGfxLine(hvg, sX, ySource, sX, peak, color); + // draw vertical from foot to peak + drawLine(hvg, sX, ySource, sX, peak, color, isReversed && doDashes); } } if (tOnScreen) { - safef(footBuf, sizeof(footBuf), "%s:%d-%d", inter->targetChrom, - inter->targetStart, inter->targetEnd); - char *footPos = cloneString(footBuf); - if (hashLookup(footHash, footPos)) - footColor = MG_BLACK; - else - hashStore(footHash, footPos); - - // draw foot of target region (2 pixels high) - hvGfxBox(hvg, tX - tWidth, yTarget, tWidth + tWidth + 1, 2, footColor); + drawFoot(hvg, inter->targetChrom, inter->targetStart, inter->targetEnd, + tX - tWidth, yTarget, tWidth + tWidth + 1, footColor, footHash); if (vis == tvDense || !sOnScreen || draw == DRAW_LINE || hvg->rc) { - // draw vertical - if (isReversed && (!tInfo->offset || draw == DRAW_ELLIPSE)) - hvGfxDottedLine(hvg, tX, yTarget, tX, peak, color, TRUE); - else - hvGfxLine(hvg, tX, yTarget, tX, peak, color); + // draw vertical from foot to peak + drawLine(hvg, tX, yTarget, tX, peak, color, isReversed && doDashes); } } if (vis == tvDense) continue; // Full mode: add map boxes and draw interaction - int highlightColor = MG_WHITE; chromStart = inter->chromStart; chromEnd = inter->chromEnd; - char *nameBuf = NULL; + if (sOnScreen) { - // add map box to source region - nameBuf = inter->sourceName; - char *clickArg = endpointClickArg(nameBuf); - if (isEmptyTextField(nameBuf)) - nameBuf = statusBuf; - //hvGfxBox(hvg, sX-1, yOff-1, 3, 3, peakColor); // needed ? - hvGfxBox(hvg, sX-1, ySource, 3, 2, peakColor); - hvGfxBox(hvg, sX, ySource, 1, 1, highlightColor); - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, - sX - sWidth, ySource, sWidth * 2, 4, - tg->track, itemBuf, nameBuf, NULL, TRUE, clickArg); + // draw grab box and map box to source region + drawFootMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->sourceName, + statusBuf, sX, ySource, sWidth, peakColor, highlightColor); } if (tOnScreen) { - // add map box to target region - nameBuf = inter->targetName; - char *clickArg = endpointClickArg(nameBuf); - if (isEmptyTextField(nameBuf)) - nameBuf = statusBuf; - //hvGfxBox(hvg, tX-1, yTarget-1, 3, 3, tInfo->isDirectional ? MG_MAGENTA : peakColor); - hvGfxBox(hvg, tX-1, yTarget, 3, 2, tInfo->isDirectional ? MG_MAGENTA : peakColor); - hvGfxBox(hvg, tX, yTarget, 1, 1, highlightColor); - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, - //tX - tWidth, yTarget, tWidth * 2, 4, - tX - tWidth, yTarget, tWidth * 2, 3, - tg->track, itemBuf, nameBuf, NULL, TRUE, clickArg); + // draw grab box and add map box to target region + drawFootMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->targetName, + statusBuf, tX, yTarget, tWidth, + tInfo->isDirectional ? MG_MAGENTA : peakColor, highlightColor); } if ((s < seqStart && t < seqStart) || (s > seqEnd && t > seqEnd)) continue; // Draw interaction and map boxes int lowerX = 0, upperX = 0; if (s < t) { lowerX = sOnScreen ? sX : xOff; upperX = tOnScreen ? tX : xOff + width; } else { lowerX = tOnScreen ? tX : xOff; upperX = sOnScreen ? sX : xOff + width; } if (draw == DRAW_LINE || !sOnScreen || !tOnScreen || hvg->rc) { // draw horizontal line between region centers at 'peak' height - if (isReversed) - hvGfxDottedLine(hvg, lowerX, peak, upperX, peak, color, TRUE); - else - hvGfxLine(hvg, lowerX, peak, upperX, peak, color); + drawLine(hvg, lowerX, peak, upperX, peak, color, isReversed && doDashes); - // map box on mid-point of horizontal line + // draw grab box and map box on mid-point of horizontal line int xMap = lowerX + (double)(upperX-lowerX)/2; - int yMap = peak-1; - hvGfxBox(hvg, xMap-1, peak-1, 3, 3, peakColor); - hvGfxBox(hvg, xMap, peak, 1, 1, highlightColor); - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, xMap-1, yMap-1, 3, 3, - tg->track, itemBuf, statusBuf, NULL, TRUE, NULL); + drawPeakMapbox(hvg, tg->track, chromStart, chromEnd, itemBuf, statusBuf, + xMap, peak, peakColor, highlightColor); continue; } // Draw curves if (draw == DRAW_CURVE) { int peakX = ((upperX - lowerX + 1) / 2) + lowerX; int peakY = peak + 30; // admittedly a hack (obscure how to define ypeak of curve) - boolean drawDashed = isReversed; - if (tInfo->offset) - drawDashed = FALSE; int maxY = hvGfxCurve(hvg, lowerX, (isReversed ? yTarget : ySource), peakX, peakY, upperX, - (isReversed ? ySource : yTarget), color, drawDashed); + (isReversed ? ySource : yTarget), color, isReversed && doDashes); // curve drawer does not use peakY as expected, so it returns actual max Y used - // draw map box on peak - hvGfxBox(hvg, peakX-1, maxY-1, 3, 3, peakColor); - hvGfxBox(hvg, peakX, maxY, 1, 1, highlightColor); - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, peakX-1, maxY-1, 3, 3, - tg->track, itemBuf, statusBuf, NULL, TRUE, NULL); + // draw grab box and map box on peak + drawPeakMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->name, statusBuf, + peakX, maxY, peakColor, highlightColor); } else if (draw == DRAW_ELLIPSE) { // can not support offsets int yLeft = yOff + peakHeight; int yTop = yOff - peakHeight; - hvGfxEllipseDraw(hvg, lowerX, yLeft, upperX, yTop, color, ELLIPSE_BOTTOM, isReversed); - // draw map box on peak + hvGfxEllipseDraw(hvg, lowerX, yLeft, upperX, yTop, color, ELLIPSE_BOTTOM, + isReversed && doDashes); + // draw grab box and map box on peak int maxY = peakHeight + yOff; int peakX = ((upperX - lowerX + 1) / 2) + lowerX; - hvGfxBox(hvg, peakX-1, maxY-1, 3, 3, peakColor); - hvGfxBox(hvg, peakX, maxY, 1, 1, highlightColor); - mapBoxHgcOrHgGene(hvg, chromStart, chromEnd, peakX-1, maxY-1, 3, 3, - tg->track, itemBuf, statusBuf, NULL, TRUE, NULL); + drawPeakMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->name, statusBuf, + peakX, maxY, peakColor, highlightColor); } } } void interactDrawLeftLabels(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) /* Override default */ { } void interactMethods(struct track *tg) /* Interact track type methods */ {