39e090676e60afcf302c046587af55b3f8926727 hiram Thu Jul 18 10:25:14 2013 -0700 checking in Robert's code refs #9741 diff --git src/hg/hgTracks/joinedRmskTrack.c src/hg/hgTracks/joinedRmskTrack.c new file mode 100644 index 0000000..a31a033 --- /dev/null +++ src/hg/hgTracks/joinedRmskTrack.c @@ -0,0 +1,1162 @@ +/* joinedRmskTrack - A comprehensive RepeatMasker visualization track + * handler. This is an extension of the original + * rmskTrack.c written by UCSC. + * + * Written by Robert Hubley 10/2012 + */ +#include "common.h" +#include "hash.h" +#include "linefile.h" +#include "jksql.h" +#include "hdb.h" +#include "hgTracks.h" +#include "rmskJoined.h" + +/* winBaseCount size ( or below ) at which the detailed view + * of repeats is turned on. + */ +#define REPEAT_DETAIL_VIEW 100000 + +/* The maximum size of unaligned sequence to draw before + * switching to the unscaled view: ie. "----//---" + */ +#define MAX_UNALIGNED_LEN 200 + +/* Hash of the repeatItems ( tg->items ) for this track. + * This is used to hold display names for the lines of + * the track, the line heights, and class colors. + */ +struct hash *classHash = NULL; +static struct repeatItem *otherRepeatItem = NULL; + +/* Hash of subtracks + * The joinedRmskTrack is designed to be used as a composite track. + * When jRepeatLoad is called this hash is populated with the + * results of one or more table queries + */ +struct hash *subTracksHash = NULL; +struct subTrack + { + /* The number of display levels used in levels[] */ + int levelCount; + /* The rmskJoined records from table query */ + struct rmskJoined *levels[30]; + }; + +// Display names +static char *rptClassNames[] = { + "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite", + "RNA", "Other", "Unknown", +}; + +// Data names +static char *rptClasses[] = { + "SINE", "LINE", "LTR", "DNA", "Simple_repeat", "Low_complexity", + "Satellite", "RNA", "Other", "Unknown", +}; + +/* Need better class to color mappings. I took these from a + * online color dictionary website: + * + * http://people.csail.mit.edu/jaffer/Color/Dictionaries + * + * I used the html4 catalog and tried to the 10 most distinct + * colors. + * + * NOTE: If these are changed, do not forget to update the + * help table in joinedRmskTrack.html + */ +static Color jRepeatClassColors[] = { + 0xff0000ff, // SINE - red + 0xff00ff00, // LINE - lime + 0xff000080, // LTR - maroon + 0xffff00ff, // DNA - fuchsia + 0xff00ffff, // Simple - yellow + 0xff008080, // LowComplex - olive + 0xffff0000, // Satellite - blue + 0xff008000, // RNA - green + 0xff808000, // Other - teal + 0xffffff00, // Unknown - aqua +}; + +/* Sort repeats by display start position. Note: We + * account for the fact we may not start the visual + * display at chromStart. See MAX_UNALIGNED_LEN. + */ +static int +cmpRepeatVisStart(const void *va, const void *vb) +{ + const struct rmskJoined *a = *((struct rmskJoined **) va); + const struct rmskJoined *b = *((struct rmskJoined **) vb); + int aStart = a->chromStart; + if (a->blockSizes[0] > MAX_UNALIGNED_LEN) + aStart = a->chromStart + (a->blockSizes[0] - MAX_UNALIGNED_LEN); + int bStart = b->chromStart; + if (b->blockSizes[0] > MAX_UNALIGNED_LEN) + bStart = b->chromStart + (b->blockSizes[0] - MAX_UNALIGNED_LEN); + return (aStart - bStart); +} + +/* Initialize the track */ +static struct repeatItem * +makeJRepeatItems() +{ + /* Initialize the subtracks hash - This will eventually contain + * all the repeat data for each displayed subtrack + */ + subTracksHash = newHash(10); + + classHash = newHash(6); + struct repeatItem *ri, *riList = NULL; + int i; + int numClasses = ArraySize(rptClasses); + for (i = 0; i < numClasses; ++i) + { + AllocVar(ri); + ri->class = rptClasses[i]; + ri->className = rptClassNames[i]; + // New color attribute + ri->color = jRepeatClassColors[i]; + slAddHead(&riList, ri); + // Hash now prebuilt to hold color attributes + hashAdd(classHash, ri->class, ri); + if (sameString(rptClassNames[i], "Other")) + otherRepeatItem = ri; + } + slReverse(&riList); + return riList; +} + +static void +jRepeatLoad(struct track *tg) +/* We do the query(ies) here so we can report how deep the track(s) + * will be when jRepeatTotalHeight() is called later on + */ +{ + tg->items = makeJRepeatItems(); + + fprintf(stderr, "Called jRepeatLoad: table = %s\n", tg->table ); + int baseWidth = winEnd - winStart; + if ( tg->visibility == tvFull && baseWidth <= REPEAT_DETAIL_VIEW) + { + struct subTrack *st = NULL; + AllocVar(st); + if ( st ) + { + st->levels[0] = NULL; + struct rmskJoined *rm = NULL; + char **row; + int rowOffset; + struct sqlConnection *conn = hAllocConn(database); + struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName, + winStart, winEnd, NULL, + &rowOffset); + + struct rmskJoined *detailList = NULL; + while ((row = sqlNextRow(sr)) != NULL) + { + rm = rmskJoinedLoad(row + rowOffset); + if (detailList) + slAddHead(detailList, rm); + else + detailList = rm; + } + slSort(&detailList, cmpRepeatVisStart); + + sqlFreeResult(&sr); + hFreeConn(&conn); + + int crChromStart, crChromEnd; + while (detailList) + { + st->levels[st->levelCount++] = detailList; + struct rmskJoined *cr = detailList; + detailList = detailList->next; + cr->next = NULL; + struct rmskJoined *prev = NULL; + rm = detailList; + int rmChromStart, rmChromEnd; + crChromStart = cr->chromStart; + if (cr->blockSizes[0] > MAX_UNALIGNED_LEN) + crChromStart = + cr->chromStart + (cr->blockSizes[0] - MAX_UNALIGNED_LEN); + crChromEnd = cr->chromEnd; + if (cr->blockSizes[cr->blockCount - 1] > MAX_UNALIGNED_LEN) + crChromEnd -= + (cr->blockSizes[cr->blockCount - 1] - MAX_UNALIGNED_LEN); + while (rm) + { + rmChromStart = rm->chromStart; + if (rm->blockSizes[0] > MAX_UNALIGNED_LEN) + rmChromStart = + rm->chromStart + (rm->blockSizes[0] - MAX_UNALIGNED_LEN); + rmChromEnd = rm->chromEnd; + if (rm->blockSizes[rm->blockCount - 1] > MAX_UNALIGNED_LEN) + rmChromEnd -= + (rm->blockSizes[rm->blockCount - 1] - MAX_UNALIGNED_LEN); + + if (rmChromStart > crChromEnd) + { + cr->next = rm; + cr = rm; + if (prev) + { + prev->next = rm->next; + } + else + { + detailList = rm->next; + } + rm = rm->next; + cr->next = NULL; + crChromStart = cr->chromStart; + if (cr->blockSizes[0] > MAX_UNALIGNED_LEN) + crChromStart = + cr->chromStart + (cr->blockSizes[0] - MAX_UNALIGNED_LEN); + crChromEnd = cr->chromEnd; + if (cr->blockSizes[cr->blockCount - 1] > MAX_UNALIGNED_LEN) + crChromEnd -= + (cr->blockSizes[cr->blockCount - 1] - MAX_UNALIGNED_LEN); + } + else + { + prev = rm; + rm = rm->next; + } + } // while ( rm ) + } // while ( detailList ) + // Create Hash Entry + hashReplace(subTracksHash, tg->table, st); + } // if ( st ) + } // if ( tg->visibility == tvFull +} + +static void +jRepeatFree(struct track *tg) +/* Free up jRepeatMasker items. */ +{ + slFreeList(&tg->items); +} + +static char * +jRepeatName(struct track *tg, void *item) +/* Return name of jRepeat item track. */ +{ + static char empty = '\0'; + struct repeatItem *ri = item; + /* + * In detail view mode the items represent different packing + * levels. No need to display a label at each level. Instead + * Just return a label for the first level. + */ + if (tg->limitedVis == tvFull && winBaseCount <= REPEAT_DETAIL_VIEW) + { + if (strcmp(ri->className, "SINE") == 0) + { + return ("Repeats"); + } + else + { + return ∅ + } + } + return ri->className; +} + +int +jRepeatTotalHeight(struct track *tg, enum trackVisibility vis) +{ + // Are we in full view mode and at the scale needed to display + // the detail view? + if (tg->limitedVis == tvFull && winBaseCount <= REPEAT_DETAIL_VIEW) + { + // Lookup the depth of this subTrack and report it + struct subTrack *st = hashFindVal(subTracksHash, tg->table ); + if ( st ) + return ( (st->levelCount+1) * 24 ); + else + // Just display one line + return ( 24 ); + } + else + return tgFixedTotalHeightNoOverflow(tg, vis); +} + +int +jRepeatItemHeight(struct track *tg, void *item) +{ + // Are we in full view mode and at the scale needed to display + // the detail view? + if (tg->limitedVis == tvFull && winBaseCount <= REPEAT_DETAIL_VIEW) + return 24; + else + return tgFixedItemHeight(tg, item); +} + +static void +drawDashedHorizLine(struct hvGfx *hvg, int x1, int x2, + int y, int dashLen, int gapLen, Color lineColor) +// ie. - - - - - - - - - - - - - - - - +{ + int cx1 = x1; + int cx2; + while (1) + { + cx2 = cx1 + dashLen; + if (cx2 > x2) + cx2 = x2; + hvGfxLine(hvg, cx1, y, cx2, y, lineColor); + cx1 += (dashLen + gapLen); + if (cx1 > x2) + break; + } +} + +static void +drawShortDashedHorizLine(struct hvGfx *hvg, int x1, int x2, + int y, int dashLen, int gapLen, Color lineColor) +// ie. - - - - - - -\\- - - - - - - - - +{ + int cx1 = x1; + int cx2; + + int midX = ((x2 - x1) / 2) + x1; + int midPointDrawn = 0; + + while (1) + { + cx2 = cx1 + dashLen; + if (cx2 > x2) + cx2 = x2; + + if (!midPointDrawn && cx2 > midX) + { + // Draw double slashes "\\" instead of dash + hvGfxLine(hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor); + hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor); + midPointDrawn = 1; + } + else + { + // Draw a dash + hvGfxLine(hvg, cx1, y, cx2, y, lineColor); + } + cx1 += dashLen; + + if (!midPointDrawn && cx1 + gapLen > midX) + { + // Draw double slashes "\\" instead of gap + hvGfxLine(hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor); + hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor); + midPointDrawn = 1; + } + cx1 += gapLen; + + if (cx1 > x2) + break; + } +} + +static void +drawNormalBox(struct hvGfx *hvg, int x1, int y1, + int width, int height, Color fillColor, Color outlineColor) +{ + struct gfxPoly *poly = gfxPolyNew(); + int y2 = y1 + height; + int x2 = x1 + width; + /* + * 1,5--------------2 + * | | + * | | + * | | + * 4--------------3 + */ + gfxPolyAddPoint(poly, x1, y1); + gfxPolyAddPoint(poly, x2, y1); + gfxPolyAddPoint(poly, x2, y2); + gfxPolyAddPoint(poly, x1, y2); + gfxPolyAddPoint(poly, x1, y1); + hvGfxDrawPoly(hvg, poly, fillColor, TRUE); + hvGfxDrawPoly(hvg, poly, outlineColor, FALSE); + gfxPolyFree(&poly); +} + +static void +drawTailBox(struct hvGfx *hvg, int x1, int y1, + int width, int height, + Color fillColor, Color outlineColor, char strand) +{ + struct gfxPoly *poly = gfxPolyNew(); + int y2 = y1 + height; + int x2 = x1 + width; + int half = (y2 - y1) / 2; + if (strand == '-') + { + + /* + * 1,6-------------2 + * | / + * | 3 + * | \ + * 5-------------4 + */ + gfxPolyAddPoint(poly, x1, y1); + gfxPolyAddPoint(poly, x2, y1); + gfxPolyAddPoint(poly, x2 - half, y1 + half); + gfxPolyAddPoint(poly, x2, y2); + gfxPolyAddPoint(poly, x1, y2); + gfxPolyAddPoint(poly, x1, y1); + } + else + { + /* + * 1,6--------------2 + * \ | + * 5 | + * / | + * 4---------------3 + */ + gfxPolyAddPoint(poly, x1, y1); + gfxPolyAddPoint(poly, x2, y1); + gfxPolyAddPoint(poly, x2, y2); + gfxPolyAddPoint(poly, x1, y2); + gfxPolyAddPoint(poly, x1 + half, y1 + half); + gfxPolyAddPoint(poly, x1, y1); + } + hvGfxDrawPoly(hvg, poly, fillColor, TRUE); + hvGfxDrawPoly(hvg, poly, outlineColor, FALSE); + gfxPolyFree(&poly); +} + +static void +drawPointBox(struct hvGfx *hvg, int x1, int y1, + int width, int height, + Color fillColor, Color outlineColor, char strand) +{ + struct gfxPoly *poly = gfxPolyNew(); + int y2 = y1 + height; + int x2 = x1 + width; + int half = (y2 - y1) / 2; + if (strand == '-') + { + + /* + * 1,6-------------2 + * / | + * 5 | + * \ | + * 4-------------3 + */ + gfxPolyAddPoint(poly, x1 + half, y1); + gfxPolyAddPoint(poly, x2, y1); + gfxPolyAddPoint(poly, x2, y2); + gfxPolyAddPoint(poly, x1 + half, y2); + gfxPolyAddPoint(poly, x1, y1 + half); + gfxPolyAddPoint(poly, x1 + half, y1); + } + else + { + /* + * 1,6--------------2 + * | \ + * | 3 + * | / + * 5--------------4 + */ + gfxPolyAddPoint(poly, x1, y1); + gfxPolyAddPoint(poly, x2 - half, y1); + gfxPolyAddPoint(poly, x2, y1 + half); + gfxPolyAddPoint(poly, x2 - half, y2); + gfxPolyAddPoint(poly, x1, y2); + gfxPolyAddPoint(poly, x1, y1); + } + hvGfxDrawPoly(hvg, poly, fillColor, TRUE); + hvGfxDrawPoly(hvg, poly, outlineColor, FALSE); + gfxPolyFree(&poly); +} + +static void +drawDirBox(struct hvGfx *hvg, int x1, int y1, int width, int height, + Color fillColor, Color outlineColor, char strand) +{ + struct gfxPoly *poly = gfxPolyNew(); + int y2 = y1 + height; + int x2 = x1 + width; + int half = (y2 - y1) / 2; + if (strand == '-') + { + + /* + * 1,7-------------2 + * / / + * 6 3 + * \ \ + * 5-------------4 + */ + gfxPolyAddPoint(poly, x1 + half, y1); + gfxPolyAddPoint(poly, x2, y1); + gfxPolyAddPoint(poly, x2 - half, y1 + half); + gfxPolyAddPoint(poly, x2, y2); + gfxPolyAddPoint(poly, x1 + half, y2); + gfxPolyAddPoint(poly, x1, y1 + half); + gfxPolyAddPoint(poly, x1 + half, y1); + } + else + { + /* + * 1,7--------------2 + * \ \ + * 6 3 + * / / + * 5---------------4 + */ + gfxPolyAddPoint(poly, x1, y1); + gfxPolyAddPoint(poly, x2 - half, y1); + gfxPolyAddPoint(poly, x2, y1 + half); + gfxPolyAddPoint(poly, x2 - half, y2); + gfxPolyAddPoint(poly, x1, y2); + gfxPolyAddPoint(poly, x1 + half, y1 + half); + gfxPolyAddPoint(poly, x1, y1); + } + hvGfxDrawPoly(hvg, poly, fillColor, TRUE); + hvGfxDrawPoly(hvg, poly, outlineColor, FALSE); + gfxPolyFree(&poly); +} + +/* + * Draw a detailed RepeatMasker annotation glyph given + * a single rmskJoined structure. + * + * A couple of caveats about the use of the rmskJoined + * structure to hold a RepeatMasker annotation. + * + * chromStart/chromEnd: These represent genomic coordinates + * that mark the extents of graphics + * annotation on the genome. I.e + * aligned blocks + unaligned consensus + * blocks. The code below may not + * draw to these extents in some cases. + * + * score: This represents the average divergence of the + * individual aligned blocks. It is a percentage + * * 100. Ie. 23.11 % is encoded as the integer 2311. + * + * blockRelStarts: Two types of blocks are interwoven in this + * format. Aligned and unaligned blocks. Aligned + * blocks will have a value >= 0 representing the + * relative position from chromStart for this + * start of this block. A value of -1 for chromStart + * is indicative of unaligned block and starts from + * the end of the last aligned block on the chromosome + * or from chromStart if it's the first block in the list. + * + * name: Stores the id#class/family + * + * rgb: Unused at this time + * + * description: Unused but the intention would be to store + * the RepeatMasker *.out lines which make up + * all the aligned sections of this joined + * annotation. + * + * blockSizes: As you would expect. + * + * Here is an example: + * ie. + * A forward strand RM annotation from chr1:186-196 + * which is aligned to a 100 bp repeat from 75-84 + * in the consensus would be represented as: + * + * chromStart: 111 + * chromEnd: 212 + * blockRelStarts: -1, 75, -1 + * blockSizes: 75, 10, 16 + * + */ +static void +drawRMGlyph(struct hvGfx *hvg, int y, int heightPer, + int width, int baseWidth, int xOff, struct rmskJoined *rm) +{ + int idx; + int lx1, lx2; + int cx1, cx2; + int w; + struct repeatItem *ri; + /* + * heightPer is the God given height of a single + * track item...respect your overlord. + */ + int alignedBlockHeight = heightPer * 0.5; + int alignedBlockOffset = heightPer - alignedBlockHeight; + int unalignedBlockHeight = heightPer * 0.75; + int unalignedBlockOffset = heightPer - unalignedBlockHeight; + + Color black = hvGfxFindColorIx(hvg, 0, 0, 0); + Color fillColor = shadesOfGray[5]; + Color outlineColor = jRepeatClassColors[0]; + + // Break apart the name and get the class of the + // repeat. + char class[256]; + // Simplify repClass for lookup: strip trailing '?', + // simplify *RNA to RNA: + char *poundPtr = index(rm->name, '#'); + if (poundPtr) + { + safecpy(class, sizeof(class), poundPtr + 1); + char *slashPtr = index(class, '/'); + if (slashPtr) + *slashPtr = '\0'; + } + else + { + safecpy(class, sizeof(class), "Unknown"); + } + char *p = &(class[strlen(class) - 1]); + if (*p == '?') + *p = '\0'; + if (endsWith(class, "RNA")) + safecpy(class, sizeof(class), "RNA"); + + // Lookup the class to get the color scheme + ri = hashFindVal(classHash, class); + if (ri == NULL) + ri = otherRepeatItem; + + // Pick the fill color based on the divergence + int percId = 10000 - rm->score; + int grayLevel = grayInRange(percId, 6000, 10000); + fillColor = shadesOfGray[grayLevel]; + outlineColor = ri->color; + + // Draw from left to right + for (idx = 0; idx < rm->blockCount; idx++) + { + int fragGStart = rm->blockRelStarts[idx]; + + /* + * Assumptions about blockCount + * - first aligned block = 1, the block 0 is + * the unaligned consnesus either 5' or 3' of + * this block. + * - The last aligned block is blockCount - 1; + */ + if (fragGStart > -1) + { + // Aligned Block + int fragSize = rm->blockSizes[idx]; + fragGStart += rm->chromStart; + int fragGEnd = fragGStart + fragSize; + lx1 = roundingScale(fragGStart - winStart, width, baseWidth) + xOff; + lx1 = max(lx1, 0); + lx2 = roundingScale(fragGEnd - winStart, width, baseWidth) + xOff; + w = lx2 - lx1; + if (w <= 0) + w = 1; + + if (idx == 1 && rm->blockCount == 3) + { + // One and only one aligned block + drawDirBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor, + rm->strand[0]); + } + else if (idx == 1) + { + // First block + if (rm->strand[0] == '-') + { + // First block on negative strand is the point block + drawPointBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor, + rm->strand[0]); + } + else + { + // First block on the positive strand is the tail block + drawTailBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor, + rm->strand[0]); + + } + } + else if (idx == (rm->blockCount - 2)) + { + // Last block + if (rm->strand[0] == '-') + { + // Last block on the negative strand is the tail block + drawTailBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor, + rm->strand[0]); + + } + else + { + // Last block on the positive strand is the poitn block + drawPointBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor, + rm->strand[0]); + + } + } + else + { + // Intermediate aligned blocks are drawn as rectangles + drawNormalBox(hvg, lx1, y + alignedBlockOffset, w, + alignedBlockHeight, fillColor, outlineColor); + } + + } + else + { + // Unaligned Block + int relStart = 0; + if (idx == 0) + { + /* + Unaligned sequence at the start of an annotation + * Draw as: + * |------------- or |------//------ + * | | + * >>>>>>> >>>>>>>> + */ + lx2 = roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + winStart, width, baseWidth) + xOff; + if (rm->blockSizes[idx] > MAX_UNALIGNED_LEN) + { + lx1 = roundingScale(rm->chromStart + + (rm->blockRelStarts[idx + 1] - + MAX_UNALIGNED_LEN) - winStart, width, + baseWidth) + xOff; + // Line Across + drawShortDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + } + else + { + lx1 = roundingScale(rm->chromStart - winStart, width, + baseWidth) + xOff; + // Line Across + drawDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + } + // Line down + hvGfxLine(hvg, lx2, y + alignedBlockOffset, lx2, + y + unalignedBlockOffset, black); + hvGfxLine(hvg, lx1, y + unalignedBlockOffset - 3, lx1, + y + unalignedBlockOffset + 3, black); + + } + else if (idx == (rm->blockCount - 1)) + { + /* + Unaligned sequence at the end of an annotation + * Draw as: + * -------------| or ------//------| + * | | + * >>>>> >>>>>>> + */ + lx1 = roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] - winStart, width, + baseWidth) + xOff; + if (rm->blockSizes[idx] > MAX_UNALIGNED_LEN) + { + lx2 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] + + MAX_UNALIGNED_LEN - winStart, width, + baseWidth) + xOff; + // Line Across + drawShortDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + } + else + { + lx2 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] + + rm->blockSizes[idx] - winStart, width, + baseWidth) + xOff; + // Line Across + drawDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + } + // Line down + hvGfxLine(hvg, lx1, y + alignedBlockOffset, lx1, + y + unalignedBlockOffset, black); + hvGfxLine(hvg, lx2, y + unalignedBlockOffset - 3, lx2, + y + unalignedBlockOffset + 3, black); + } + else + { + /* + Middle Unaligned + * Draw unaligned sequence between aligned fragments + * as: ......... + * / \ + * >>>>> >>>>>> + * + * or: + * ............. + * \ / + * >>>>> >>>>>> + * + * Also use ....//.... to indicate not to scale if + * necessary. + * + */ + int alignedGapSize = rm->blockRelStarts[idx + 1] - + (rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1]); + int unaSize = rm->blockSizes[idx]; + + int alignedOverlapSize = unaSize - alignedGapSize; + + if (unaSize < 0) + { + relStart = + rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1] - + (alignedOverlapSize / 2); + + if (abs(unaSize) > rm->blockSizes[idx - 1]) + unaSize = -rm->blockSizes[idx - 1]; + + lx1 = + roundingScale(rm->chromStart + + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] + + unaSize - winStart, width, baseWidth) + xOff; + lx1 = max(lx1, 0); + lx2 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + winStart, width, baseWidth) + xOff; + + int midPoint = ((lx2 - lx1) / 2) + lx1; + + // Block Intersection Line + hvGfxLine(hvg, lx1, y + alignedBlockOffset, lx1, y + + alignedBlockOffset + alignedBlockHeight, black); + + // Line Up + hvGfxLine(hvg, lx1, y + alignedBlockOffset, midPoint, y + + unalignedBlockOffset, black); + + // Line Down + hvGfxLine(hvg, lx2, y + alignedBlockOffset, midPoint, + y + unalignedBlockOffset, black); + + } + else + if (alignedOverlapSize > 0 && + ((alignedOverlapSize * 0.5) > + (0.3 * rm->blockSizes[idx - 1]) + || (alignedOverlapSize * 0.5) > + (0.3 * rm->blockSizes[idx + 1]))) + { + // Need to shorten unaligned length + int smallOverlapLen = 0; + smallOverlapLen = (0.3 * rm->blockSizes[idx - 1]); + if (smallOverlapLen > (0.3 * rm->blockSizes[idx + 1])) + smallOverlapLen = (0.3 * rm->blockSizes[idx + 1]); + unaSize = (smallOverlapLen * 2) + alignedGapSize; + relStart = + rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1] - + smallOverlapLen; + lx1 = + roundingScale(relStart + rm->chromStart - winStart, + width, baseWidth) + xOff; + lx1 = max(lx1, 0); + lx2 = + roundingScale(relStart + rm->chromStart + unaSize - + winStart, width, baseWidth) + xOff; + // Line Up + cx1 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] - winStart, width, + baseWidth) + xOff; + hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1, + y + unalignedBlockOffset, black); + + // Line Across + drawShortDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + // Line Down + cx2 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + winStart, width, baseWidth) + xOff; + hvGfxLine(hvg, cx2, y + alignedBlockOffset, lx2, + y + unalignedBlockOffset, black); + } + else + { + // Adequate to display full length + relStart = + rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1] - + (alignedOverlapSize / 2); + lx1 = + roundingScale(relStart + rm->chromStart - winStart, + width, baseWidth) + xOff; + lx1 = max(lx1, 0); + lx2 = + roundingScale(relStart + rm->chromStart + unaSize - + winStart, width, baseWidth) + xOff; + // Line Up + int cx1 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] - winStart, + width, baseWidth) + xOff; + hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1, y + + unalignedBlockOffset, black); + drawDashedHorizLine(hvg, lx1, lx2, + y + unalignedBlockOffset, 5, 5, black); + // Line Down + cx2 = + roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + winStart, width, baseWidth) + xOff; + hvGfxLine(hvg, cx2, y + alignedBlockOffset, lx2, + y + unalignedBlockOffset, black); + } + } + } + } + +} + +/* This is the original repeat drawing routine, modified + * to handle the new rmskJoined table structure. + */ +static void +origRepeatDraw(struct track *tg, int seqStart, int seqEnd, + struct hvGfx *hvg, int xOff, int yOff, int width, + MgFont * font, Color color, enum trackVisibility vis) +{ + int baseWidth = seqEnd - seqStart; + struct repeatItem *ri; + int y = yOff; + int heightPer = tg->heightPer; + int lineHeight = tg->lineHeight; + int x1, x2, w; + boolean isFull = (vis == tvFull); + Color col; + struct sqlConnection *conn = hAllocConn(database); + struct sqlResult *sr = NULL; + char **row; + int rowOffset; + + if (isFull) + { + /* + Do gray scale representation spread out among tracks. + */ + struct hash *hash = newHash(6); + struct rmskJoined *ro; + int percId; + int grayLevel; + + for (ri = tg->items; ri != NULL; ri = ri->next) + { + ri->yOffset = y; + y += lineHeight; + hashAdd(hash, ri->class, ri); + } + sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, + &rowOffset); + while ((row = sqlNextRow(sr)) != NULL) + { + ro = rmskJoinedLoad(row + rowOffset); + char class[256]; + // Simplify repClass for lookup: strip trailing '?', + // simplify *RNA to RNA: + char *poundPtr = index(ro->name, '#'); + if (poundPtr) + { + safecpy(class, sizeof(class), poundPtr + 1); + char *slashPtr = index(class, '/'); + if (slashPtr) + *slashPtr = '\0'; + } + else + { + safecpy(class, sizeof(class), "Unknown"); + } + char *p = &(class[strlen(class) - 1]); + if (*p == '?') + *p = '\0'; + if (endsWith(class, "RNA")) + safecpy(class, sizeof(class), "RNA"); + ri = hashFindVal(hash, class); + if (ri == NULL) + ri = otherRepeatItem; + percId = 10000 - ro->score; + grayLevel = grayInRange(percId, 6000, 10000); + col = shadesOfGray[grayLevel]; + + int idx = 0; + for (idx = 0; idx < ro->blockCount; idx++) + { + if (ro->blockRelStarts[idx] > 0) + { + int blockStart = ro->chromStart + ro->blockRelStarts[idx]; + int blockEnd = + ro->chromStart + ro->blockRelStarts[idx] + ro->blockSizes[idx]; + + x1 = roundingScale(blockStart - winStart, width, baseWidth) + xOff; + x1 = max(x1, 0); + x2 = roundingScale(blockEnd - winStart, width, baseWidth) + xOff; + w = x2 - x1; + if (w <= 0) + w = 1; + hvGfxBox(hvg, x1, ri->yOffset, w, heightPer, col); + } + } + rmskJoinedFree(&ro); + } + freeHash(&hash); + } + else + { + char table[64]; + boolean hasBin; + struct dyString *query = newDyString(1024); + /* + * Do black and white on single track. Fetch less than + * we need from database. + */ + if (hFindSplitTable(database, chromName, tg->table, table, &hasBin)) + { + dyStringPrintf(query, + "select chromStart,blockCount,blockSizes," + "blockRelStarts from %s where ", + table); + if (hasBin) + hAddBinToQuery(winStart, winEnd, query); + dyStringPrintf(query, "chromStart<%u and chromEnd>%u ", winEnd, + winStart); + /* + * if we're using a single rmsk table, add chrom to the where clause + */ + if (startsWith("rmskJoined", table)) + dyStringPrintf(query, " and chrom = '%s' ", chromName); + sr = sqlGetResult(conn, query->string); + while ((row = sqlNextRow(sr)) != NULL) + { + int idx = 0; + int blockCount = sqlSigned(row[1]); + int sizeOne; + int *blockSizes; + int *blockRelStarts; + int chromStart = sqlUnsigned(row[0]); + sqlSignedDynamicArray(row[2], &blockSizes, &sizeOne); + assert(sizeOne == blockCount); + sqlSignedDynamicArray(row[3], &blockRelStarts, &sizeOne); + assert(sizeOne == blockCount); + + for (idx = 1; idx < blockCount - 1; idx++) + { + if (blockRelStarts[idx] >= 0) + { + int blockStart = chromStart + blockRelStarts[idx]; + int blockEnd = chromStart + blockRelStarts[idx] + blockSizes[idx]; + + x1 = + roundingScale(blockStart - winStart, width, baseWidth) + xOff; + x1 = max(x1, 0); + x2 = roundingScale(blockEnd - winStart, width, baseWidth) + xOff; + w = x2 - x1; + if (w <= 0) + w = 1; + hvGfxBox(hvg, x1, yOff, w, heightPer, MG_BLACK); + } + } + } + } + dyStringFree(&query); + } + sqlFreeResult(&sr); + hFreeConn(&conn); +} + +/* Main callback for displaying this track in the viewport + * of the browser. + */ +static void +jRepeatDraw(struct track *tg, int seqStart, int seqEnd, + struct hvGfx *hvg, int xOff, int yOff, int width, + MgFont * font, Color color, enum trackVisibility vis) +{ + int baseWidth = seqEnd - seqStart; + /* + * Its unclear to me why heightPer is not updated to the + * value set in jRepeatItemHeight() at the time this callback + * is invoked. Getting the correct value myself. + * was: + * int heightPer = tg->heightPer; + */ + int heightPer = jRepeatItemHeight(tg, NULL); + boolean isFull = (vis == tvFull); + struct rmskJoined *rm; + + // If we are in full view mode and the scale is sufficient, + // display the new visualization. + if (isFull && baseWidth <= REPEAT_DETAIL_VIEW) + { + int level = yOff; + + struct subTrack *st = hashFindVal(subTracksHash, tg->table ); + if ( ! st ) + return; + int lidx = st->levelCount; + int currLevel = 0; + for (currLevel = 0; currLevel < lidx; currLevel++) + { + rm = st->levels[currLevel]; + while (rm) + { + drawRMGlyph(hvg, level, heightPer, width, baseWidth, xOff, rm); + + char statusLine[128]; + int ss1 = roundingScale(rm->alignStart - winStart, + width, baseWidth) + xOff; + + safef(statusLine, sizeof(statusLine), "name: %s", rm->name ); + + int x1 = + roundingScale(rm->alignStart - winStart, width, baseWidth) + xOff; + x1 = max(x1, 0); + int x2 = + roundingScale(rm->alignEnd - winStart, width, baseWidth) + xOff; + int w = x2 - x1; + if (w <= 0) + w = 1; + + mapBoxHc(hvg, rm->alignStart, rm->alignEnd, ss1, level, w, heightPer, + tg->track, rm->id, statusLine); + rm = rm->next; + } + level += heightPer; + rmskJoinedFreeList(&(st->levels[currLevel])); + } + } + else + { + // Draw the stereotypical view + origRepeatDraw(tg, seqStart, seqEnd, + hvg, xOff, yOff, width, font, color, vis); + } +} + +void +jRepeatMethods(struct track *tg) +{ + tg->loadItems = jRepeatLoad; + tg->freeItems = jRepeatFree; + tg->drawItems = jRepeatDraw; + tg->colorShades = shadesOfGray; + tg->itemName = jRepeatName; + tg->mapItemName = jRepeatName; + tg->totalHeight = jRepeatTotalHeight; + tg->itemHeight = jRepeatItemHeight; + tg->itemStart = tgItemNoStart; + tg->itemEnd = tgItemNoEnd; + tg->mapsSelf = TRUE; +}