46734f435d23f6919a08661b386c5a70a01a51cc kate Tue Sep 25 12:00:32 2018 -0700 Add support for 'merged mode' track display. This mode merges directional interactions by source or target, and displays as linked features (not arcs). This was originally conceived by Max, and initially implemented as the pack mode for standard interaction display, but was extended as different track display in all modes so that arcs and linked features display can be viewed concurrently, By request of JK. refs #21917 diff --git src/hg/hgTracks/interactTrack.c src/hg/hgTracks/interactTrack.c index da67bef..fdd3f6b 100644 --- src/hg/hgTracks/interactTrack.c +++ src/hg/hgTracks/interactTrack.c @@ -1,744 +1,767 @@ /* interactTrack -- draw interaction between two genomic regions */ /* Copyright (C) 2018 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "obscure.h" #include "hgTracks.h" #include "bedCart.h" #include "bigWarn.h" #include "interact.h" #include "interactUi.h" static int interactTotalHeight(struct track *tg, enum trackVisibility vis) /* calculate height of all the interactions being displayed */ { if ( tg->visibility == tvDense) return tl.fontHeight; int min, max, deflt, current; cartTdbFetchMinMaxPixels(cart, tg->tdb, INTERACT_MINHEIGHT, INTERACT_MAXHEIGHT, atoi(INTERACT_DEFHEIGHT), &min, &max, &deflt, ¤t); return tg->height = current; } static Color interactItemColor(struct track *tg, void *item, struct hvGfx *hvg, int scoreMin, int scoreMax) /* Return color to draw an interaction */ { struct interact *inter = item; if (tg->colorShades) { struct bed *bed = (struct bed *)inter; adjustBedScoreGrayLevel(tg->tdb, bed, scoreMin, scoreMax); return tg->colorShades[grayInRange(inter->score, 0, 1000)]; } /* There must be a better way..., e.g.: unsigned red = COLOR_32_RED(inter->color); unsigned green = COLOR_32_GREEN(inter->color); unsigned blue = COLOR_32_BLUE(inter->color); */ unsigned red = (inter->color & 0xff0000) >> 16; unsigned green = (inter->color & 0xff00) >> 8; unsigned blue = inter->color & 0xff; return hvGfxFindColorIx(hvg, red, green, blue); } boolean interactSourceInWindow(struct interact *inter) /* True if midpoint of source is on screen */ { unsigned s = interactRegionCenter(inter->sourceStart, inter->sourceEnd); return (s >= winStart) && (s < winEnd); } boolean interactTargetInWindow(struct interact *inter) /* True if midpoint of target is on screen */ { unsigned t = interactRegionCenter(inter->targetStart, inter->targetEnd); return (t >= winStart) && (t < winEnd); } static void loadAndFilterItems(struct track *tg) /* Load all interact items in region */ { loadSimpleBedWithLoader(tg, (bedItemLoader)interactLoadAndValidate); if (slCount(tg->items) == 0 && tg->limitedVisSet) { // too many items to display // borrowed behaviors in bamTrack and vcfTrack // TODO BRANEY: make this behavior generic for bigBeds // (bigBedSelectRange) tg->drawItems = bigDrawWarning; tg->networkErrMsg = "Too many items in display (zoom in)"; tg->totalHeight = bigWarnTotalHeight; return; } // filters struct interact *inter, *next, *filteredItems = NULL; int count = slCount(tg->items); // exclude if missing endpoint(s) in window char *endsVisible = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, INTERACT_ENDS_VISIBLE, INTERACT_ENDS_VISIBLE_DEFAULT); for (inter = tg->items; inter; inter = next) { next = inter->next; if (differentString(endsVisible, INTERACT_ENDS_VISIBLE_ANY)) { boolean sOnScreen = interactSourceInWindow(inter); boolean tOnScreen = interactTargetInWindow(inter); if (sameString(endsVisible, INTERACT_ENDS_VISIBLE_TWO)) { if (!(sOnScreen && tOnScreen)) continue; } if (sameString(endsVisible, INTERACT_ENDS_VISIBLE_ONE)) { if (!(sOnScreen || tOnScreen)) continue; } } slAddHead(&filteredItems, inter); } slReverse(&filteredItems); // consider sorting by score/value so highest scored items draw last (on top) if (slCount(filteredItems) != count) labelTrackAsFiltered(tg); tg->items = filteredItems; } 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 interactFreeItems(struct track *tg) /* Free up interact items track */ { interactFreeList((struct interact **)(&tg->items)); } -static boolean isBedMode(struct track *tg) -/* Pack and squish modes display using BED linked features code */ -{ -return tg->visibility == tvPack || tg->visibility == tvSquish; -} - static struct linkedFeatures *interactToLf(struct interact *inter, boolean doColor) /* Convert interact BED to linkedFeatures */ { struct bed *bed = interactToBed(inter); struct linkedFeatures *lf = lfFromBed(bed); +// not sure why this is needed -- lfFromBed seems to reorder blocks, sometimes ? +linkedFeaturesSortAndBound(lf); if (doColor) { lf->extra = (void *)USE_ITEM_RGB; /* signal for coloring */ lf->filterColor = bed->itemRgb; } bedFree(&bed); // TODO: use lfFromBedExtra with scoreMin, scoreMax ? return lf; } -static boolean isMergeMode(struct track *tg) -/* Packed mode with merging on targets */ +static boolean isLinkedFeaturesMode(struct track *tg) +/* Determine if linked feature display will be used */ +{ +return (tg->visibility == tvPack || tg->visibility == tvSquish); +} + +int interactSize(struct interact *inter) +/* Compute length of interaction (distance between middle of each region) in bp */ +{ +int sourceCenter = 0, targetCenter = 0; +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; +} + + +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 */ { -return cartBoolean(cart, "interactTargetMerge"); +int yOff = getYOff(tg); +int flipped = yOff + tg->height + yOff - y; +return flipped; +} + +struct interactTrackInfo { + boolean isDirectional; // source and target are distinct item types + char *offset; // which end to draw offset (source or target) + char *mergeMode; // merge by source or target (or no merge if NULL) + 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; + +static void interactGetLayoutInfo(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 = tg->customPt; +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) + tInfo->maxSize = size; + } + else + { + tInfo->otherCount++; + // suppress interchromosomal labels if they overlap + if (!tInfo->doOtherLabels) + continue; + int labelWidth = vgGetFontStringWidth(hvg->vg, font, otherChrom); + int x = getX(inter->chromStart, seqStart, scale, xOff); + int labelStart = round((double)(x - labelWidth)/2); + int labelEnd = labelStart + labelWidth - 1; + 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; } void interactLoadItems(struct track *tg) /* Load interact items in interact format */ { +struct interactTrackInfo *tInfo = NULL; +AllocVar(tInfo); +tInfo->mergeMode = interactUiMergeMode(cart, tg->track, tg->tdb); +tg->customPt = tInfo; + loadAndFilterItems(tg); -if (isBedMode(tg)) +if (tInfo->mergeMode || isLinkedFeaturesMode(tg)) { // convert to BEDs for linked feature display struct interact *inters = tg->items, *inter; struct linkedFeatures *lfs = NULL, *lf; struct hash *intersMerge = hashNew(0); - boolean doMerge = isMergeMode(tg); boolean doColor = !tg->colorShades; for (inter = inters; inter; inter = inter->next) { - if (doMerge) + if (tInfo->mergeMode) { - lf = (struct linkedFeatures *) hashFindVal(intersMerge, inter->targetName); + boolean byTarget = sameString(tInfo->mergeMode, INTERACT_MERGE_TARGET); + // hash by source or target name + char *name = (byTarget ? inter->targetName : inter->sourceName); + lf = (struct linkedFeatures *) hashFindVal(intersMerge, name); if (lf) { - // add a simple feature for this source to the linked feature + // add a simple feature for the other end (source or target) to the linked feature struct simpleFeature *sf = NULL; AllocVar(sf); - sf->start = inter->sourceStart; - sf->end = inter->sourceEnd; + sf->start = (byTarget ? inter->sourceStart : inter->targetStart); + sf->end = (byTarget ? inter->sourceEnd : inter->targetEnd); struct simpleFeature *sfs = lf->components; slAddHead(&sfs, sf); lf->components = sfs; + if (lf->filterColor != inter->color) + lf->filterColor = MG_GRAY; // TODO: consider averaging score - -/* - // FIXME: just for demo - struct bed *tempBed = interactToBed(inter); - if (orientFromChar(tempBed->strand[0]) != lf->orientation) - lf->orientation = 0; - bedFree(&tempBed); -*/ } else { // create a linked feature for this target lf = interactToLf(inter, doColor); - lf->name = inter->targetName; - lf->tallStart = inter->targetStart; - lf->tallEnd = inter->targetEnd; lf->orientation = 0; + lf->name = (byTarget ? inter->targetName : inter->sourceName); + lf->tallStart = (byTarget ? inter->targetStart : inter->sourceStart); + lf->tallEnd = (byTarget ? inter->targetEnd : inter->sourceEnd); hashAdd(intersMerge, lf->name, lf); } } else { lf = interactToLf(inter, doColor); slAddHead(&lfs, lf); } } - if (doMerge) + if (tInfo->mergeMode) { // sort simplefeatures and adjust bounds of merged features struct hashEl *el, *els = hashElListHash(intersMerge); for (el = els; el; el = el->next) { lf = (struct linkedFeatures *)el->val; linkedFeaturesSortAndBound(lf); slAddHead(&lfs, lf); } slSort(&lfs, linkedFeaturesCmp); } else { slReverse(&lfs); } tg->items = lfs; // TODO: consider freeing interact items } else { tg->mapsSelf = TRUE; tg->totalHeight = interactTotalHeight; tg->drawLeftLabels = interactDrawLeftLabels; tg->freeItems = interactFreeItems; } } char *interactMouseover(struct interact *inter, char *otherChrom) /* Make mouseover text for an interaction */ { struct dyString *ds = dyStringNew(0); if (isEmptyTextField(inter->name)) { if (!isEmptyTextField(inter->exp)) dyStringPrintf(ds, "%s ", inter->exp); if (otherChrom) dyStringPrintf(ds, "%s", otherChrom); else { char buf[4096]; sprintLongWithCommas(buf, inter->chromEnd - inter->chromStart); dyStringPrintf(ds, "%s bp", buf); } } else dyStringPrintf(ds, "%s", inter->name); if (inter->score) dyStringPrintf(ds, " %d", inter->score); if (inter->value != 0.0) dyStringPrintf(ds, " %0.2f", inter->value); return dyStringCannibalize(&ds); } int regionFootWidth(int start, int end, double scale) /* Return half foot width in pixels */ { unsigned size = end - start; int width = scale * (double)size / 2; if (width == 0) width = 1; return width; } -int interactSize(struct interact *inter) -/* Compute length of interaction (distance between middle of each region) in bp */ -{ -int sourceCenter = 0, targetCenter = 0; -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) - tInfo->maxSize = size; - } - else - { - tInfo->otherCount++; - // suppress interchromosomal labels if they overlap - if (!tInfo->doOtherLabels) - continue; - int labelWidth = vgGetFontStringWidth(hvg->vg, font, otherChrom); - int x = getX(inter->chromStart, seqStart, scale, xOff); - int labelStart = round((double)(x - labelWidth)/2); - int labelEnd = labelStart + labelWidth - 1; - 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; -} - -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); } 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); } 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, tg->track, item, itemBuf, NULL, TRUE, clickArg); } 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, tg->track, item, status, NULL, TRUE, NULL); } static void drawInteractItems(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. */ +/* Draw a list of interact items with connectors (e.g. curves) */ { #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]; // Gather info for layout -struct interactTrackInfo *tInfo = interactGetTrackInfo(tg, seqStart, hvg, xOff, font, scale); +interactGetLayoutInfo(tg, seqStart, hvg, xOff, font, scale); +struct interactTrackInfo *tInfo = (struct interactTrackInfo *)tg->customPt; 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; // 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 #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; if (otherChrom) { // different chromosomes // draw below same chrom items, if any int height = 0; int yOffOther = yOff; 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(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(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(tg, hvg, inter->chromStart, inter->chromEnd, nameBuf, statusBuf, x - footWidth, yOffOther, footWidth, peakColor, highlightColor, drawUp); // add map box to vertical mapBoxHgcOrHgGene(hvg, inter->chromStart, inter->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, inter->chromStart, inter->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); 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); int tWidth = regionFootWidth(inter->targetStart,inter->targetEnd, scale); boolean tOnScreen = (t >= seqStart) && (t< seqEnd); boolean isReversed = (tInfo->isDirectional && t < s); int interSize = abs(t - s); int peakHeight = (tInfo->sameHeight - 15) * ((double)interSize / tInfo->maxSize) + 10; int peak = yOff + peakHeight; if (vis == tvDense) peak = yOff + tg->height; // 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) { 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(tg, hvg, sX, ySource, sX, peak, color, isReversed && doDashes, drawUp); } } if (tOnScreen) { 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(tg, hvg, tX, yTarget, tX, peak, color, isReversed && doDashes, drawUp); } } if (vis == tvDense) continue; // Full mode: add map boxes and draw interaction if (sOnScreen) { // draw grab box and map box to source region 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(tg, hvg, inter->chromStart, inter->chromEnd, inter->targetName, statusBuf, tX, yTarget, tWidth, 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(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(tg, hvg, inter->chromStart, inter->chromEnd, itemBuf, statusBuf, xMap, peak, peakColor, highlightColor, drawUp); continue; } // Draw curves if (draw == DRAW_CURVE) { int peakX = ((upperX - lowerX + 1) / 2) + lowerX; 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); // curve drawer does not use peakY as expected, so it returns actual max Y used // draw grab box and map box on peak if (drawUp) maxY = (maxY - peakY)/2 + tg->customInt; 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; int ellipseOrient = ELLIPSE_BOTTOM; if (drawUp) { ellipseOrient = ELLIPSE_TOP; yLeft = yOff + tg->height - peakHeight; yTop = yOff + tg->height + peakHeight; } hvGfxEllipseDraw(hvg, lowerX, yLeft, upperX, yTop, color, ellipseOrient, isReversed && doDashes); // draw grab box and map box on peak int maxY = peakHeight + yOff; int peakX = ((upperX - lowerX + 1) / 2) + lowerX; drawPeakMapbox(tg, hvg, inter->chromStart, inter->chromEnd, inter->name, statusBuf, peakX, maxY, peakColor, highlightColor, drawUp); } } } void interactLinkedFeaturesDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw a item with target in contrasting color */ { +struct linkedFeatures *lf = item; +if (tg->visibility == tvDense) + { + lf->filterColor = slightlyDarkerColor(hvg, MG_GRAY); + // can't distinguish overlapping colors, so force to gray + } linkedFeaturesDrawAt(tg, item, hvg, xOff, y, scale, font, color, vis); +struct interactTrackInfo *tInfo = tg->customPt; -if (isMergeMode(tg)) +// draw overlapping items in white +if (tInfo->mergeMode) { - struct linkedFeatures *lf = item; - // TODO: use magenta if track isn't colored ? - drawScaledBox(hvg, lf->tallStart, lf->tallEnd, scale, xOff, y, tg->heightPer, MG_BLACK); + struct simpleFeature *sf; + int shortHeight = tg->heightPer/2; + for (sf = lf->components; sf; sf = sf->next) + { + if (sf->start > lf->tallStart && sf->end < lf->tallEnd) + { + drawScaledBox(hvg, sf->start, sf->end, scale, xOff, y + shortHeight/2, + shortHeight, MG_WHITE); + } + } + } +else + { + struct simpleFeature *sf1 = lf->components, *sf2 = sf1->next; + if (sf2->start < sf1->end) + { + drawScaledBox(hvg, sf2->start, sf2->end, scale, xOff, y, tg->heightPer, MG_WHITE); + } } } 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. */ { -if (isBedMode(tg)) +struct interactTrackInfo *tInfo = (struct interactTrackInfo *)tg->customPt; +if (tInfo->mergeMode || isLinkedFeaturesMode(tg)) { tg->drawItemAt = interactLinkedFeaturesDrawAt; linkedFeaturesDraw(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } else + // curve, etc. connector display drawInteractItems(tg, seqStart, seqEnd, hvg, xOff, yOff, width, font, color, vis); } void interactMethods(struct track *tg) /* Interact track type methods */ { tg->bedSize = 12; -linkedFeaturesMethods(tg); // for pack and squish modes +linkedFeaturesMethods(tg); // for most vis and mode settings tg->loadItems = interactLoadItems; tg->drawItems = interactDrawItems; } void interactCtMethods(struct track *tg) /* Interact track methods for custom track */ { interactMethods(tg); }