227226fa2553637314e05bec16a320063220aecc kate Wed Aug 29 17:41:30 2018 -0700 First cut flipped display. Doesn't yet flip ellipses, and mapbox on curves sometimes misplaced. refs #21917 diff --git src/hg/hgTracks/interactTrack.c src/hg/hgTracks/interactTrack.c index f4d40fd..a2432c9 100644 --- src/hg/hgTracks/interactTrack.c +++ src/hg/hgTracks/interactTrack.c @@ -231,83 +231,113 @@ if (labelStart <= prevLabelEnd && !(labelStart == prevLabelStart && labelEnd == prevLabelEnd && sameString(otherChrom, prevLabel))) tInfo->doOtherLabels = FALSE; prevLabelStart = labelStart; prevLabelEnd = labelEnd; prevLabel = otherChrom; } } tInfo->fontHeight = vgGetFontPixelHeight(hvg->vg, font); tInfo->otherHeight = (tInfo->otherCount) ? 3 * tInfo->fontHeight : 0; tInfo->sameHeight = (tInfo->sameCount) ? tg->height - tInfo->otherHeight : 0; return tInfo; } -void drawFoot(struct hvGfx *hvg, char *seq, int seqStart, int seqEnd, - int x, int y, int width, Color color, struct hash *footHash) +static void setYOff(struct track *tg, int yOff) +/* Stash y offset for this track */ +{ +tg->customInt = yOff; +} + +static int getYOff(struct track *tg) +/* Get y offset for this track (stashed by DrawItems) */ +{ +return tg->customInt; +} + +static int flipY(struct track *tg, int y) +/* Invert y coordinate if flipped display is requested */ +{ +int yOff = getYOff(tg); +int flipped = yOff + tg->height + yOff - y; +return flipped; +} + +static void drawFoot(struct track *tg, struct hvGfx *hvg, char *seq, int seqStart, int seqEnd, + int x, int y, int width, Color color, boolean drawUp, 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); +if (drawUp) + y = flipY(tg, y) - 2; hvGfxBox(hvg, x, y, width, 2, footColor); } -void drawLine(struct hvGfx *hvg, int x1, int y1, int x2, int y2, Color color, boolean isDashed) +static void drawLine(struct track *tg, struct hvGfx *hvg, int x1, int y1, int x2, int y2, + Color color, boolean isDashed, boolean drawUp) /* Draw vertical or horizontal */ { +if (drawUp) + { + y1 = flipY(tg, y1); + y2 = flipY(tg, y2); + } 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) +static void drawFootMapbox(struct track *tg, struct hvGfx *hvg, int start, int end, char *item, char *status, + int x, int y, int width, Color peakColor, Color highlightColor, boolean drawUp) /* Draw grab box and add map box */ { // Add var to identify endpoint ('foot'), or NULL if no name for endpoint */ char *clickArg = NULL; if (!isEmptyTextField(item)) { char buf[256]; safef(buf, sizeof(buf),"foot=%s", cgiEncode(item)); clickArg = cloneString(buf); } char *itemBuf = isEmptyTextField(item) ? status : item; +if (drawUp) + y = flipY(tg, y) - 3; 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); + tg->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) +void drawPeakMapbox(struct track *tg, struct hvGfx *hvg, int seqStart, int seqEnd, char *item, char *status, + int x, int y, Color peakColor, Color highlightColor, boolean drawUp) /* Draw grab box and add map box */ { +if (drawUp) + y = flipY(tg, y); 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); + tg->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) @@ -317,31 +347,37 @@ 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); +setYOff(tg, yOff); // TODO: better to stash this in tInfo, and save that in track struct */ int highlightColor = MG_WHITE; +boolean drawUp = trackDbSettingClosestToHomeOn(tg->tdb, INTERACT_UP) && vis == tvFull; +#define DEBUG +#ifdef DEBUG +drawUp = TRUE; +#endif // 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); // 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 @@ -364,55 +400,57 @@ 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) - drawFoot(hvg, inter->chrom, inter->chromStart, inter->chromEnd, - x - footWidth, yOffOther, footWidth + footWidth + 1, color, footHashOther); + drawFoot(tg, hvg, inter->chrom, inter->chromStart, inter->chromEnd, + x - footWidth, yOffOther, footWidth + footWidth + 1, color, drawUp, footHashOther); // draw the vertical boolean isReversed = tInfo->isDirectional && differentString(inter->chrom, inter->sourceChrom); - drawLine(hvg, x, yOffOther, x, yPos, color, isReversed && doDashes); + drawLine(tg, hvg, x, yOffOther, x, yPos, color, isReversed && doDashes, drawUp); if (vis == tvDense) continue; // add map box to foot char *nameBuf = (inter->chromStart == inter->sourceStart ? inter->sourceName : inter->targetName); - drawFootMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, nameBuf, statusBuf, - x - footWidth, yOffOther, footWidth, peakColor, highlightColor); + drawFootMapbox(tg, hvg, inter->chromStart, inter->chromEnd, nameBuf, statusBuf, + x - footWidth, yOffOther, footWidth, peakColor, highlightColor, drawUp); // 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); + if (drawUp) + yPos = flipY(tg, yPos); 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, NULL); } continue; } // Draw same chromosome interaction // source region unsigned s = interactRegionCenter(inter->sourceStart, inter->sourceEnd); @@ -438,119 +476,130 @@ 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) { - drawFoot(hvg, inter->sourceChrom, inter->sourceStart, inter->sourceEnd, - sX - sWidth, ySource, sWidth + sWidth + 1, footColor, footHash); + drawFoot(tg, hvg, inter->sourceChrom, inter->sourceStart, inter->sourceEnd, + sX - sWidth, ySource, sWidth + sWidth + 1, footColor, drawUp, footHash); if (vis == tvDense || !tOnScreen || draw == DRAW_LINE || hvg->rc) { // draw vertical from foot to peak - drawLine(hvg, sX, ySource, sX, peak, color, isReversed && doDashes); + drawLine(tg, hvg, sX, ySource, sX, peak, color, isReversed && doDashes, drawUp); } } if (tOnScreen) { - drawFoot(hvg, inter->targetChrom, inter->targetStart, inter->targetEnd, - tX - tWidth, yTarget, tWidth + tWidth + 1, footColor, footHash); + drawFoot(tg, hvg, inter->targetChrom, inter->targetStart, inter->targetEnd, + tX - tWidth, yTarget, tWidth + tWidth + 1, footColor, drawUp, footHash); if (vis == tvDense || !sOnScreen || draw == DRAW_LINE || hvg->rc) { // draw vertical from foot to peak - drawLine(hvg, tX, yTarget, tX, peak, color, isReversed && doDashes); + drawLine(tg, hvg, tX, yTarget, tX, peak, color, isReversed && doDashes, drawUp); } } if (vis == tvDense) continue; // Full mode: add map boxes and draw interaction chromStart = inter->chromStart; chromEnd = inter->chromEnd; if (sOnScreen) { // 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); + drawFootMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->sourceName, + statusBuf, sX, ySource, sWidth, peakColor, highlightColor, drawUp); } if (tOnScreen) { // draw grab box and add map box to target region - drawFootMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->targetName, + drawFootMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->targetName, statusBuf, tX, yTarget, tWidth, - tInfo->isDirectional ? MG_MAGENTA : peakColor, highlightColor); + tInfo->isDirectional ? MG_MAGENTA : peakColor, highlightColor, drawUp); } 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 - drawLine(hvg, lowerX, peak, upperX, peak, color, isReversed && doDashes); + drawLine(tg, hvg, lowerX, peak, upperX, peak, color, isReversed && doDashes, drawUp); // draw grab box and map box on mid-point of horizontal line int xMap = lowerX + (double)(upperX-lowerX)/2; - drawPeakMapbox(hvg, tg->track, chromStart, chromEnd, itemBuf, statusBuf, - xMap, peak, peakColor, highlightColor); + drawPeakMapbox(tg, hvg, chromStart, chromEnd, itemBuf, statusBuf, + xMap, peak, peakColor, highlightColor, drawUp); 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) - int maxY = hvGfxCurve(hvg, lowerX, - (isReversed ? yTarget : ySource), peakX, peakY, upperX, - (isReversed ? ySource : yTarget), color, isReversed && doDashes); + //int fudge = drawUp ? -30 : 30; + int fudge = 30; + int peakY = peak + fudge; // admittedly a hack (obscure how to define ypeak of curve) + int y1 = isReversed ? yTarget : ySource; + int y2 = isReversed ? ySource : yTarget; + if (drawUp) + { + y1 = flipY(tg, y1); + y2 = flipY(tg, y2); + peakY = flipY(tg, peakY); + } + int maxY = hvGfxCurve(hvg, lowerX, y1, peakX, peakY, upperX, y2, color, isReversed && doDashes); + if (drawUp) + maxY = maxY - fudge - 5; // curve drawer does not use peakY as expected, so it returns actual max Y used // draw grab box and map box on peak - drawPeakMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->name, statusBuf, - peakX, maxY, peakColor, highlightColor); + drawPeakMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->name, statusBuf, + peakX, maxY, peakColor, highlightColor, drawUp); } else if (draw == DRAW_ELLIPSE) { // can not support offsets int yLeft = yOff + peakHeight; int yTop = yOff - peakHeight; + // TODO: flipped mode 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; - drawPeakMapbox(hvg, tg->track, inter->chromStart, inter->chromEnd, inter->name, statusBuf, - peakX, maxY, peakColor, highlightColor); + drawPeakMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->name, statusBuf, + peakX, maxY, peakColor, highlightColor, drawUp); } } } 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 */ {