fb4e1734e17994eda5cd3bdc96b8d9a540001cd3 hiram Wed Jul 16 11:15:27 2014 -0700 updating code from Robert refs #9741 diff --git src/hg/hgTracks/joinedRmskTrack.c src/hg/hgTracks/joinedRmskTrack.c index 461664d..6271adf 100644 --- src/hg/hgTracks/joinedRmskTrack.c +++ src/hg/hgTracks/joinedRmskTrack.c @@ -1,537 +1,533 @@ /* 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) 2013 The Regents of the University of California +/* 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" -/* winBaseCount size ( or below ) at which the detailed view - * of repeats is turned on. + +/* 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 REPEAT_DETAIL_VIEW 100000 +#define DETAIL_VIEW_MAX_SCALE 30000 -/* The maximum size of unaligned sequence to draw before - * switching to the unscaled view: ie. "----//---" +/* 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_LEN 200 +#define MAX_UNALIGNED_PIXEL_LEN 150 + +// TODO: Document +#define LABEL_PADDING 5 + + +// TODO: Document +static float pixelsPerBase = 1.0; -/* classHash is the hash of the repeatItems ( tg->items ) for this track. +/* 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. - * - * the subTracksHash is a hash of subtracks + */ +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 itemSpecifics -/* extra information to go with each track */ - { - struct hash *classHash; - struct repeatItem *otherRepeatItem; - struct hash *subTracksHash; - }; +struct hash *subTracksHash = NULL; struct subTrack { - /* The number of display levels used in levels[] */ + // The number of display levels used in levels[] int levelCount; - /* The rmskJoined records from table query */ + // The rmskJoined records from table query struct rmskJoined *levels[30]; }; -// Display names + +// Repeat Classes: Display names static char *rptClassNames[] = { "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite", "RNA", "Other", "Unknown", }; -// Data names +// Repeat Classes: 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 + +/* 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 */ 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 }; +// 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) /* 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. + * display at chromStart. See MAX_UNALIGNED_PIXEL_LEN. */ { - 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); +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 */ -static void makeJRepeatItems(struct track *tg) - /* Initialize the subtracks hash - This will eventually contain - * all the repeat data for each displayed subtrack - */ { - struct itemSpecifics *extraData; - AllocVar(extraData); - - extraData->classHash = newHash(6); - extraData->otherRepeatItem = NULL; - extraData->subTracksHash = newHash(10); - tg->extraUiData = extraData; - +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(extraData->classHash, ri->class, ri); + hashAdd (classHash, ri->class, ri); if (sameString (rptClassNames[i], "Other")) - extraData->otherRepeatItem = ri; + otherRepeatItem = ri; } slReverse (&riList); - tg->items = 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 + * will be when jRepeatTotalHeight() is called later on. */ { - makeJRepeatItems(tg); - struct itemSpecifics *extraData = tg->extraUiData; + /* + * 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; - if ( tg->visibility == tvFull && baseWidth <= REPEAT_DETAIL_VIEW) +pixelsPerBase = (float) insideWidth / (float) baseWidth; +if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE) { struct subTrack *st = NULL; AllocVar (st); if (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); + slAddHead (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; + int rmChromStart, rmChromEnd; 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); + + struct Extents *ext = NULL; + ext = getExtents (cr); + crChromStart = ext->start; + crChromEnd = ext->end; + 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); + 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; - 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 { + // Save for a lower level prev = rm; rm = rm->next; } } // while ( rm ) } // while ( detailList ) // Create Hash Entry - hashReplace(extraData->subTracksHash, tg->table, st); + 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 (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE) { if (strcmp (ri->className, "SINE") == 0) { return ("Repeats"); } else { return ∅ } } return ri->className; } -static int jRepeatTotalHeight(struct track *tg, enum trackVisibility vis) +int jRepeatTotalHeight (struct track *tg, enum trackVisibility vis) { - struct itemSpecifics *extraData = tg->extraUiData; // Are we in full view mode and at the scale needed to display // the detail view? - if (tg->limitedVis == tvFull && winBaseCount <= REPEAT_DETAIL_VIEW) +if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE) { // Lookup the depth of this subTrack and report it - struct subTrack *st = hashFindVal(extraData->subTracksHash, tg->table ); + 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); } -static int jRepeatItemHeight(struct track *tg, void *item) +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) +if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE) 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. - - - - - - -\\- - - - - - - - - +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; - int midX = ((x2 - x1) / 2) + x1; +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: + * 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 > midX) + 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, + 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 drawTailBox(struct hvGfx *hvg, int x1, int y1, - int width, int height, - Color fillColor, Color outlineColor, char strand) +static void drawBoxWChevrons (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); +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 @@ -558,33 +554,30 @@ * * 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, - struct itemSpecifics *extraData) { 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; @@ -604,33 +597,33 @@ 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(extraData->classHash, class); +ri = hashFindVal (classHash, class); if (ri == NULL) - ri = extraData->otherRepeatItem; +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 @@ -642,320 +635,356 @@ { // 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]); + 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 - drawPointBox(hvg, lx1, y + alignedBlockOffset, w, - alignedBlockHeight, fillColor, outlineColor, - rm->strand[0]); + drawBoxWChevrons (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]); + 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 - drawTailBox(hvg, lx1, y + alignedBlockOffset, w, - alignedBlockHeight, fillColor, outlineColor, - rm->strand[0]); + drawBoxWChevrons (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]); + drawBoxWChevrons (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); + 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 + * Unaligned sequence at the start of an annotation * Draw as: * |------------- or |------//------ * | | * >>>>>>> >>>>>>>> */ - lx2 = roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + lx2 = roundingScale (rm->chromStart + + rm->blockRelStarts[idx + 1] - winStart, width, baseWidth) + xOff; - if (rm->blockSizes[idx] > MAX_UNALIGNED_LEN) + if ((rm->blockSizes[idx] * pixelsPerBase) > + MAX_UNALIGNED_PIXEL_LEN) { lx1 = roundingScale (rm->chromStart + (rm->blockRelStarts[idx + 1] - - MAX_UNALIGNED_LEN) - winStart, width, - baseWidth) + xOff; + (int) + (MAX_UNALIGNED_PIXEL_LEN / + pixelsPerBase)) - + winStart, width, baseWidth) + xOff; // Line Across - drawShortDashedHorizLine(hvg, lx1, lx2, - y + unalignedBlockOffset, 5, 5, black); + drawDashedHorizLineWHash (hvg, lx1, lx2, + y + + unalignedBlockOffset, + 5, 5, black, rm->blockSizes[idx]); } else { - lx1 = roundingScale(rm->chromStart - winStart, width, - baseWidth) + xOff; + 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, + stringWidth, fontHeight, MG_BLACK, font, + rm->name); + + } else if (idx == (rm->blockCount - 1)) { /* - Unaligned sequence at the end of an annotation + * 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) + 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] + + lx2 = roundingScale (rm->chromStart + + rm->blockRelStarts[idx - + 1] + rm->blockSizes[idx - 1] + - MAX_UNALIGNED_LEN - winStart, width, - baseWidth) + xOff; + (int) + (MAX_UNALIGNED_PIXEL_LEN / + pixelsPerBase) - winStart, + width, baseWidth) + xOff; // Line Across - drawShortDashedHorizLine(hvg, lx1, lx2, - y + unalignedBlockOffset, 5, 5, black); + drawDashedHorizLineWHash (hvg, lx1, lx2, + y + + unalignedBlockOffset, + 5, 5, black, rm->blockSizes[idx]); } else { - lx2 = - roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + + lx2 = roundingScale (rm->chromStart + + rm->blockRelStarts[idx - + 1] + rm->blockSizes[idx - 1] + - rm->blockSizes[idx] - winStart, width, - baseWidth) + xOff; + 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 + * 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); + 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] + + lx1 = roundingScale (rm->chromStart + + rm->blockRelStarts[idx - + 1] + rm->blockSizes[idx - 1] + - unaSize - winStart, width, baseWidth) + xOff; + unaSize - winStart, width, + baseWidth) + xOff; lx1 = max (lx1, 0); - lx2 = - roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + 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); + hvGfxLine (hvg, lx1, y + alignedBlockOffset, lx1, + y + alignedBlockOffset + alignedBlockHeight, + black); // Line Up - hvGfxLine(hvg, lx1, y + alignedBlockOffset, midPoint, y + - unalignedBlockOffset, black); + hvGfxLine (hvg, lx1, y + alignedBlockOffset, + midPoint, y + unalignedBlockOffset, black); // Line Down - hvGfxLine(hvg, lx2, y + alignedBlockOffset, midPoint, - y + unalignedBlockOffset, black); + hvGfxLine (hvg, lx2, y + alignedBlockOffset, + midPoint, y + unalignedBlockOffset, black); } - else - if (alignedOverlapSize > 0 && + 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 - + rm->blockRelStarts[idx - 1] + + rm->blockSizes[idx - 1] - smallOverlapLen; + lx1 = roundingScale (relStart + rm->chromStart - winStart, width, baseWidth) + xOff; - // Line Up - cx1 = - roundingScale(rm->chromStart + rm->blockRelStarts[idx - 1] + - rm->blockSizes[idx - 1] - winStart, width, + 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); + drawDashedHorizLineWHash (hvg, lx1, lx2, + y + + unalignedBlockOffset, + 5, 5, black, rm->blockSizes[idx]); // Line Down - cx2 = - roundingScale(rm->chromStart + rm->blockRelStarts[idx + 1] - + 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 - + 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] + + roundingScale (rm->chromStart + + rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1] - winStart, width, baseWidth) + xOff; - hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1, y + - unalignedBlockOffset, black); + 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] - + 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) +/* This is the original repeat drawing routine, modified + * to handle the new rmskJoined table structure. + */ { - struct itemSpecifics *extraData = tg->extraUiData; 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. + * 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) @@ -971,175 +1000,183 @@ 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 = extraData->otherRepeatItem; + 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]; + ro->chromStart + ro->blockRelStarts[idx] + + ro->blockSizes[idx]; - x1 = roundingScale(blockStart - winStart, width, baseWidth) + xOff; + x1 = roundingScale (blockStart - winStart, width, + baseWidth) + xOff; x1 = max (x1, 0); - x2 = roundingScale(blockEnd - winStart, width, baseWidth) + xOff; + 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); + "blockRelStarts from %s where ", table); if (hasBin) hAddBinToQuery (winStart, winEnd, query); - dyStringPrintf(query, "chromStart<%u and chromEnd>%u ", winEnd, - winStart); + 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]; + int blockEnd = + chromStart + blockRelStarts[idx] + blockSizes[idx]; - x1 = - roundingScale(blockStart - winStart, width, baseWidth) + xOff; + x1 = roundingScale (blockStart - winStart, + width, baseWidth) + xOff; x1 = max (x1, 0); - x2 = roundingScale(blockEnd - winStart, width, baseWidth) + xOff; + 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) { - struct itemSpecifics *extraData = tg->extraUiData; 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 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) +if (isFull && baseWidth <= DETAIL_VIEW_MAX_SCALE) { int level = yOff; - struct subTrack *st = hashFindVal(extraData->subTracksHash, tg->table ); + 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, extraData); + 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 ); + safef (statusLine, sizeof (statusLine), "%s", rm->name); - int x1 = - roundingScale(rm->alignStart - winStart, width, baseWidth) + xOff; + int x1 = roundingScale (rm->alignStart - winStart, width, + baseWidth) + xOff; x1 = max (x1, 0); - int x2 = - roundingScale(rm->alignEnd - winStart, width, baseWidth) + xOff; + 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); + 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)