38cc0731871ba38d4278bf5acc3e22663c778253 hiram Fri Oct 17 09:24:56 2014 -0700 fixup font sizing issues refs #9741 diff --git src/hg/hgTracks/rmskJoinedTrack.c src/hg/hgTracks/rmskJoinedTrack.c index d69d324..8642be6 100644 --- src/hg/hgTracks/rmskJoinedTrack.c +++ src/hg/hgTracks/rmskJoinedTrack.c @@ -1,1196 +1,1216 @@ /* 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 * * Modifications: * * 7/2014 * With the help of Jim Kent we modified the * glyph design to more closely resemble the * existing browser styles. */ /* Copyright (C) 2014 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "hdb.h" #include "hgTracks.h" #include "rmskJoined.h" /* DETAIL_VIEW_MAX_SCALE: The size ( max bp ) at which the * detailed view of repeats is turned on, otherwise it reverts * back to the original UCSC glyph. */ #define DETAIL_VIEW_MAX_SCALE 30000 /* MAX_UNALIGNED_PIXEL_LEN: The maximum size of unaligned sequence * to draw before switching to the unscaled view: ie. "----/###/---" * This is measured in pixels *not* base pairs due to the * mixture of scaled ( lines ) and unscaled ( text ) glyph * components. */ #define MAX_UNALIGNED_PIXEL_LEN 150 // TODO: Document #define LABEL_PADDING 5 - +#define MINHEIGHT 24 // TODO: Document static float pixelsPerBase = 1.0; /* 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 rmskJoinedLoadItems 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]; }; // Repeat Classes: Display names static char *rptClassNames[] = { "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite", "RNA", "Other", "Unknown", }; // Repeat Classes: Data names static char *rptClasses[] = { "SINE", "LINE", "LTR", "DNA", "Simple_repeat", "Low_complexity", "Satellite", "RNA", "Other", "Unknown", }; /* Repeat 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 */ // trying colors from snakePalette static Color rmskJoinedClassColors[] = { 0xff1f77b4, // SINE - red 0xffff7f0e, // LINE - lime 0xff2ca02c, // LTR - maroon 0xffd62728, // DNA - fuchsia 0xff9467bd, // Simple - yellow 0xff8c564b, // LowComplex - olive 0xffe377c2, // Satellite - blue 0xff7f7f7f, // RNA - green 0xffbcbd22, // Other - teal 0xff17becf, // Unknown - aqua }; /* static Color rmskJoinedClassColors[] = { 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 }; */ // Basic range type struct Extents { int start; int end; }; static struct Extents * getExtents(struct rmskJoined *rm) /* Calculate the glyph extents in genome coordinate * space ( ie. bp ) * * It is not straightforward to determine the extent * of this track's glyph. This is due to the mixture * of graphic and text elements within the glyph. * * TODO: Consider changing to graphics (ie. pixel) space. */ { static struct Extents ex; char lenLabel[20]; if (rm == NULL) return NULL; ex.start = rm->alignStart - (int) ((mgFontStringWidth(tl.font, rm->name) + LABEL_PADDING) / pixelsPerBase); if ((rm->blockSizes[0] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN) { safef(lenLabel, sizeof(lenLabel), "%d", rm->blockSizes[0]); ex.start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) + (int) ((mgFontStringWidth(tl.font, lenLabel) + LABEL_PADDING) / pixelsPerBase); } else { ex.start -= rm->blockSizes[0]; } ex.end = rm->alignEnd; if ((rm->blockSizes[rm->blockCount - 1] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN) { safef(lenLabel, sizeof(lenLabel), "%d", rm->blockSizes[rm->blockCount - 1]); ex.end += (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) + (int) ((mgFontStringWidth(tl.font, lenLabel) + LABEL_PADDING) / pixelsPerBase); } else { ex.end += rm->blockSizes[rm->blockCount - 1]; } return &ex; } -static int cmpRepeatVisStart(const void *va, const void *vb) +// A better way to organize the display +static int cmpRepeatDiv(const void *va, const void *vb) +/* Sort repeats by divergence. + */ +{ +struct rmskJoined *a = *((struct rmskJoined **) va); +struct rmskJoined *b = *((struct rmskJoined **) vb); + +return (b->score - a->score); +} + +//static int cmpRepeatVisStart(const void *va, const void *vb) /* Sort repeats by display start position. Note: We * account for the fact we may not start the visual * display at chromStart. See MAX_UNALIGNED_PIXEL_LEN. */ +/* { struct rmskJoined *a = *((struct rmskJoined **) va); struct rmskJoined *b = *((struct rmskJoined **) vb); struct Extents *ext = NULL; ext = getExtents(a); int aStart = ext->start; ext = getExtents(b); int bStart = ext->start; return (aStart - bStart); } +*/ static struct repeatItem * makeJRepeatItems() /* Initialize the track */ { 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 = rmskJoinedClassColors[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 rmskJoinedLoadItems(struct track *tg) /* We do the query(ies) here so we can report how deep the track(s) * will be when rmskJoinedTotalHeight() is called later on. */ { /* * Initialize the subtracks hash - This will eventually contain * all the repeat data for each displayed subtrack. */ if (!subTracksHash) subTracksHash = newHash(20); tg->items = makeJRepeatItems(); int baseWidth = winEnd - winStart; pixelsPerBase = (float) insideWidth / (float) baseWidth; if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE) { struct subTrack *st = NULL; AllocVar(st); st->levels[0] = NULL; st->levelCount = 0; 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); slAddHead(&detailList, rm); } - slSort(&detailList, cmpRepeatVisStart); + //slSort(&detailList, cmpRepeatVisStart); + slSort(&detailList, cmpRepeatDiv); sqlFreeResult(&sr); hFreeConn(&conn); int crChromStart, crChromEnd; while (detailList) { st->levels[st->levelCount++] = detailList; struct rmskJoined *cr = detailList; detailList = detailList->next; cr->next = NULL; int rmChromStart, rmChromEnd; struct rmskJoined *prev = NULL; rm = detailList; struct Extents *ext = NULL; ext = getExtents(cr); crChromStart = ext->start; crChromEnd = ext->end; while (rm) { ext = getExtents(rm); rmChromStart = ext->start; rmChromEnd = ext->end; if (rmChromStart > crChromEnd) { cr->next = rm; cr = rm; crChromStart = rmChromStart; crChromEnd = rmChromEnd; if (prev) prev->next = rm->next; else detailList = rm->next; rm = rm->next; cr->next = NULL; } else { // Save for a lower level prev = rm; rm = rm->next; } } // while ( rm ) } // while ( detailList ) // Create Hash Entry hashReplace(subTracksHash, tg->table, st); } // if ( tg->visibility == tvFull } static void rmskJoinedFreeItems(struct track *tg) /* Free up rmskJoinedMasker items. */ { slFreeList(&tg->items); } static char * rmskJoinedName(struct track *tg, void *item) /* Return name of rmskJoined 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 <= DETAIL_VIEW_MAX_SCALE) { if (strcmp(ri->className, "SINE") == 0) return("Repeats"); else return ∅ } return ri->className; } -#define HEIGHT24 24 -int rmskJoinedTotalHeight(struct track *tg, enum trackVisibility vis) +int rmskJoinedItemHeight(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 <= DETAIL_VIEW_MAX_SCALE) { - // Lookup the depth of this subTrack and report it - struct subTrack *st = hashFindVal(subTracksHash, tg->table); - if (st) - return ((st->levelCount + 1) * HEIGHT24); + if ( tg->heightPer < MINHEIGHT ) + return MINHEIGHT; else - return (HEIGHT24); // Just display one line + return tg->heightPer; } else -return tgFixedTotalHeightNoOverflow(tg, vis); + { + return tgFixedItemHeight(tg, item); + } } -int rmskJoinedItemHeight(struct track *tg, void *item) +int rmskJoinedTotalHeight(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 <= DETAIL_VIEW_MAX_SCALE) - return HEIGHT24; + { + // Lookup the depth of this subTrack and report it + struct subTrack *st = hashFindVal(subTracksHash, tg->table); + if (st) + return ((st->levelCount + 1) * rmskJoinedItemHeight(tg, NULL) ); else - return tgFixedItemHeight(tg, item); + return (rmskJoinedItemHeight(tg, NULL)); // Just display one line + } +else +return tgFixedTotalHeightNoOverflow(tg, vis); } 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 drawDashedHorizLineWHash(struct hvGfx *hvg, int x1, int x2, int y, int dashLen, int gapLen, Color lineColor, int unalignedLen) // ie. - - - - - - -\234\- - - - - - - - - { int cx1 = x1; int cx2; char lenLabel[20]; safef(lenLabel, sizeof(lenLabel), "%d", unalignedLen); MgFont *font = tl.font; int fontHeight = tl.fontHeight; int stringWidth = mgFontStringWidth(font, lenLabel) + LABEL_PADDING; int glyphWidth = x2 - x1; if (glyphWidth < stringWidth + 6 + (2 * dashLen)) stringWidth = 0; int midX = ((glyphWidth) / 2) + x1; int startHash = midX - (stringWidth * 0.5); int midPointDrawn = 0; /* - Degrade Gracefully: + * Degrade Gracefully: * Too little space to draw dashes or even * hash marks, give up. */ if (glyphWidth < 6 + dashLen) { hvGfxLine(hvg, x1, y, x2, y, lineColor); return; } if (glyphWidth < 6 + (2 * dashLen)) { midX -= 3; hvGfxLine(hvg, x1, y, midX, y, lineColor); hvGfxLine(hvg, midX, y - 3, midX + 3, y + 3, lineColor); hvGfxLine(hvg, midX + 3, y - 3, midX + 6, y + 3, lineColor); hvGfxLine(hvg, midX + 6, y, x2, y, lineColor); return; } while (1) { cx2 = cx1 + dashLen; if (cx2 > x2) cx2 = x2; if (!midPointDrawn && cx2 > startHash) { // Draw double slashes "\\" instead of dash hvGfxLine(hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor); if (stringWidth) { - hvGfxTextCentered(hvg, cx1 + 3, y - 3, stringWidth, + hvGfxTextCentered(hvg, cx1 + 3, y - (fontHeight/2), stringWidth, fontHeight, MG_BLACK, font, lenLabel); cx1 += stringWidth; } hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor); cx1 += 6; 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); if (stringWidth) { hvGfxTextCentered(hvg, cx1 + 3, y - 3, stringWidth, fontHeight, MG_BLACK, font, lenLabel); cx1 += stringWidth; } hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor); cx1 += 6; midPointDrawn = 1; } else { 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 drawBoxWChevrons(struct hvGfx *hvg, int x1, int y1, int width, int height, Color fillColor, Color outlineColor, char strand) { drawNormalBox(hvg, x1, y1, width, height, fillColor, outlineColor); static Color white = 0xffffffff; int dir = (strand == '+' ? 1 : -1); int midY = y1 + ((height) >> 1); clippedBarbs(hvg, x1 + 1, midY, width, ((height >> 1) - 2), 5, dir, white, TRUE); } static void drawRMGlyph(struct hvGfx *hvg, int y, int heightPer, int width, int baseWidth, int xOff, struct rmskJoined *rm) /* * 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 * */ { 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 = rmskJoinedClassColors[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 drawBoxWChevrons(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 drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w, alignedBlockHeight, fillColor, outlineColor, rm->strand[0]); } else { // First block on the positive strand is the tail block drawBoxWChevrons(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 drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w, alignedBlockHeight, fillColor, outlineColor, rm->strand[0]); } else { // Last block on the positive strand is the poitn block drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w, alignedBlockHeight, fillColor, outlineColor, rm->strand[0]); } } else { // Intermediate aligned blocks are drawn as rectangles drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w, alignedBlockHeight, fillColor, outlineColor, rm->strand[0]); } } 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] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN) { lx1 = roundingScale(rm->chromStart + (rm->blockRelStarts[idx + 1] - (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase)) - winStart, width, baseWidth) + xOff; // Line Across drawDashedHorizLineWHash(hvg, lx1, lx2, y + unalignedBlockOffset, 5, 5, black, rm->blockSizes[idx]); } 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); // Draw labels MgFont *font = tl.font; int fontHeight = tl.fontHeight; int stringWidth = mgFontStringWidth(font, rm->name) + LABEL_PADDING; hvGfxTextCentered(hvg, lx1 - stringWidth, - y + unalignedBlockOffset + fontHeight, + heightPer - fontHeight + y, stringWidth, fontHeight, MG_BLACK, font, rm->name); } 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] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN) { lx2 = roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1] + (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) - winStart, width, baseWidth) + xOff; // Line Across drawDashedHorizLineWHash(hvg, lx1, lx2, y + unalignedBlockOffset, 5, 5, black, rm->blockSizes[idx]); } 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 drawDashedHorizLineWHash(hvg, lx1, lx2, y + unalignedBlockOffset, 5, 5, black, rm->blockSizes[idx]); // 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); } } } } } 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) /* This is the original repeat drawing routine, modified * to handle the new rmskJoined table structure. */ { 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 grayscale 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)) { sqlDyStringPrintf(query, "select chromStart,blockCount,blockSizes," "blockRelStarts from %s where ", table); if (hasBin) hAddBinToQuery(winStart, winEnd, query); sqlDyStringPrintf(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)) sqlDyStringPrintf(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 rmskJoinedDrawItems(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; // TODO: Document pixelsPerBase = (float) width / (float) baseWidth; /* * Its unclear to me why heightPer is not updated to the * value set in rmskJoinedItemHeight() at the time this callback * is invoked. Getting the correct value myself. * was: * int heightPer = tg->heightPer; */ int heightPer = rmskJoinedItemHeight(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 <= DETAIL_VIEW_MAX_SCALE) { 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), "%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 rmskJoinedMethods(struct track *tg) { tg->loadItems = rmskJoinedLoadItems; tg->freeItems = rmskJoinedFreeItems; tg->drawItems = rmskJoinedDrawItems; tg->colorShades = shadesOfGray; tg->itemName = rmskJoinedName; tg->mapItemName = rmskJoinedName; tg->totalHeight = rmskJoinedTotalHeight; tg->itemHeight = rmskJoinedItemHeight; tg->itemStart = tgItemNoStart; tg->itemEnd = tgItemNoEnd; tg->mapsSelf = TRUE; }