8c908f948b09826c6cb4452ee5b282aca41be85e galt Tue Dec 8 21:52:59 2015 -0800 Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it. diff --git src/hg/hgTracks/rmskJoinedTrack.c src/hg/hgTracks/rmskJoinedTrack.c index dce1ba6..7c9a231 100644 --- src/hg/hgTracks/rmskJoinedTrack.c +++ src/hg/hgTracks/rmskJoinedTrack.c @@ -39,44 +39,101 @@ /* 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; + +struct windowGlobals +{ +struct windowGlobals *next; +struct window *window; +/* These are the static globals needed for each window: */ +struct hash *classHash; +struct repeatItem *otherRepeatItem; +struct hash *subTracksHash; +}; + +static struct windowGlobals *wgWindows = NULL; +static struct windowGlobals *wgWindow = NULL; +static struct windowGlobals *wgWindowOld = NULL; + +static bool setWgWindow() +/* scan genoCache windows. create new one if not found */ +{ +if (wgWindow && wgWindow->window == currentWindow) + return FALSE; +wgWindowOld = wgWindow; +for (wgWindow = wgWindows; wgWindow; wgWindow = wgWindow->next) + { + if (wgWindow->window == currentWindow) + { + return TRUE; + } + } +AllocVar(wgWindow); +wgWindow->window = currentWindow; +slAddTail(&wgWindows, wgWindow); +return TRUE; +} + +// per window static vars act like local global vars, need one per window + /* 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; + +static void setWg() +/* set up per-window globals for the current window */ +{ +if (setWgWindow()) + { + if (wgWindowOld) + { + wgWindowOld->classHash = classHash; + wgWindowOld->otherRepeatItem = otherRepeatItem; + wgWindowOld->subTracksHash = subTracksHash; + } + classHash = wgWindow->classHash; + otherRepeatItem = wgWindow->otherRepeatItem; + subTracksHash = wgWindow->subTracksHash; + } + +} + + + struct subTrack { // The number of display levels used in levels[] int levelCount; // The rmskJoined records from table query struct rmskJoined **levels; }; // Repeat Classes: Display names static char *rptClassNames[] = { "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite", "RNA", "Other", "Unknown", }; @@ -94,115 +151,109 @@ * help table in joinedRmskTrack.html */ 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 }; -// Basic range type -struct Extents -{ -int start; -int end; -}; -static struct Extents * getExtents(struct rmskJoined *rm) +static void getExtents(struct rmskJoined *rm, int *pStart, int *pEnd) /* 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. * */ { -static struct Extents ex; +int start; +int end; char lenLabel[20]; -if (rm == NULL) - return NULL; - // Start position is anchored by the alignment start // coordinates. Then we subtract from that space for // the label. -ex.start = rm->alignStart - +start = rm->alignStart - (int) ((mgFontStringWidth(tl.font, rm->name) + LABEL_PADDING) / pixelsPerBase); // Now subtract out space for the unaligned sequence // bar starting off the graphics portion of the glyph. if ((rm->blockSizes[0] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN) { // Unaligned sequence bar needs length label -- account for that. safef(lenLabel, sizeof(lenLabel), "%d", rm->blockSizes[0]); - ex.start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) + + start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) + (int) ((mgFontStringWidth(tl.font, lenLabel) + LABEL_PADDING) / pixelsPerBase); } else { - ex.start -= rm->blockSizes[0]; + start -= rm->blockSizes[0]; } -ex.end = rm->alignEnd; +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 += + end += (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) + (int) ((mgFontStringWidth(tl.font, lenLabel) + LABEL_PADDING) / pixelsPerBase); } else { - ex.end += rm->blockSizes[rm->blockCount - 1]; + end += rm->blockSizes[rm->blockCount - 1]; } -return &ex; +*pStart = start; +*pEnd = end; } // A better way to organize the display static int cmpRepeatDiv(const void *va, const void *vb) /* Sort repeats by divergence. TODO: Another way to do this would be to sort by containment. How many annotations are contained within the repeat. This would require a "contained_count" be precalculated for each annotation. */ { struct rmskJoined *a = *((struct rmskJoined **) va); struct rmskJoined *b = *((struct rmskJoined **) vb); return (b->score - a->score); } struct repeatItem *classRIList = NULL; struct repeatItem *fullRIList = NULL; static struct repeatItem * makeJRepeatItems() /* Initialize the track */ { +setWg(); int i; struct repeatItem *ri = NULL; // Build a permanent hash for mapping // class names to colors. Used by glyph // drawing routine. Also build the // classRIList primarily for use in tvDense // mode. if (!classHash) { classHash = newHash(6); int numClasses = ArraySize(rptClasses); for (i = 0; i < numClasses; ++i) { AllocVar(ri); @@ -221,208 +272,210 @@ return classRIList; } 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. */ +setWg(); + if (!subTracksHash) subTracksHash = newHash(20); //tg->items = makeJRepeatItems(); makeJRepeatItems(); int baseWidth = winEnd - winStart; pixelsPerBase = (float) insideWidth / (float) baseWidth; //Disabled //if ((tg->visibility == tvFull || tg->visibility == tvSquish || // tg->visibility == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE) if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE) { struct repeatItem *ri = NULL; struct subTrack *st = NULL; AllocVar(st); 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); } + //warn("rmskJoinedLoadItems tg->table=%s chromName=%s winStart=%d winEnd=%d, rowOffset=%d slCount(detailList)=%d", + //tg->table, chromName, winStart, winEnd, rowOffset, slCount(detailList)); // DEBUG REMOVE slSort(&detailList, cmpRepeatDiv); sqlFreeResult(&sr); hFreeConn(&conn); - AllocArray( st->levels, slCount(&detailList)); + if (detailList) + AllocArray( st->levels, slCount(detailList)); - int crChromEnd; while (detailList) { st->levels[st->levelCount++] = detailList; struct rmskJoined *cr = detailList; detailList = detailList->next; cr->next = NULL; + int crChromStart, crChromEnd; int rmChromStart, rmChromEnd; struct rmskJoined *prev = NULL; rm = detailList; - struct Extents *ext = NULL; - ext = getExtents(cr); - crChromEnd = ext->end; - + getExtents(cr, &crChromStart, &crChromEnd); if ( tg->visibility == tvFull ) { AllocVar(ri); ri->className = cr->name; slAddHead(&fullRIList, ri); } //Disabled //if ( tg->visibility == tvSquish || tg->visibility == tvPack ) if ( tg->visibility == tvFull ) { while (rm) { - ext = getExtents(rm); - rmChromStart = ext->start; - rmChromEnd = ext->end; + getExtents(rm, &rmChromStart, &rmChromEnd); if (rmChromStart > crChromEnd) { cr->next = rm; cr = rm; 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 ) } // else if ( tg->visibility == tvSquish ... } // while ( detailList ) // Create Hash Entry + //warn("rmskJoinedLoadItems tg->table=%s st->levelCount=%d", tg->table, st->levelCount); // DEBUG REMOVE hashReplace(subTracksHash, tg->table, st); slReverse(&fullRIList); tg->items = fullRIList; } // if ((tg->visibility == tvFull || ... else tg->items = classRIList; +//warn("rmskJoinedLoadItems track=%s vis=%d slCount(tg->items)=%d", tg->track, tg->visibility, slCount(tg->items)); // DEBUG REMOVE } 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. */ //Disabled //if ((tg->visibility == tvSquish || // tg->visibility == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE) if (tg->visibility == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE) { if (strcmp(ri->className, "SINE") == 0) - return("Repeats"); + return "Repeats"; else - return ∅ + return ""; } return ri->className; } int rmskJoinedItemHeight(struct track *tg, void *item) { // Are we in full view mode and at the scale needed to display // the detail view? //Disabled //if (tg->limitedVis == tvSquish && winBaseCount <= DETAIL_VIEW_MAX_SCALE ) // { // if ( tg->heightPer < (MINHEIGHT/2) ) // return (MINHEIGHT/2); // else // return tg->heightPer; // } //else if ((tg->limitedVis == tvFull || tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE) if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE) { - if ( tg->heightPer < MINHEIGHT ) - return MINHEIGHT; - else - return tg->heightPer; + return max(tg->heightPer, MINHEIGHT); } else { return tgFixedItemHeight(tg, item); } } int rmskJoinedTotalHeight(struct track *tg, enum trackVisibility vis) { +setWg(); +//warn("rmskJoinedTotalHeight track=%s vis=%d winBaseCount=%d", tg->track, vis, winBaseCount); // DEBUG REMOVE // Are we in full view mode and at the scale needed to display // the detail view? //Disabled //if ((tg->limitedVis == tvFull || tg->limitedVis == tvSquish || // tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE) 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) { - tg->height = ((st->levelCount ) * rmskJoinedItemHeight(tg, NULL) ); - return ((st->levelCount ) * rmskJoinedItemHeight(tg, NULL)); + tg->height = (st->levelCount * rmskJoinedItemHeight(tg, NULL)); + //warn("rmskJoinedTotalHeight track=%s st->levelCount=%d tg->height=%d", tg->track, st->levelCount, tg->height); // DEBUG REMOVE + return tg->height; } else { tg->height = rmskJoinedItemHeight(tg, NULL); - return (rmskJoinedItemHeight(tg, NULL)); // Just display one line + //warn("rmskJoinedTotalHeight track=%s st is NULL tg->height=%d", tg->track, tg->height); // DEBUG REMOVE + return tg->height; // Just display one line } } else { if ( vis == tvDense ) { tg->height = tgFixedTotalHeightNoOverflow(tg, tvDense ); return tgFixedTotalHeightNoOverflow(tg, tvDense ); } else { tg->height = tgFixedTotalHeightNoOverflow(tg, tvFull ); return tgFixedTotalHeightNoOverflow(tg, tvFull ); } } @@ -617,30 +670,31 @@ * 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 * */ { +setWg(); 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); @@ -1010,30 +1064,31 @@ 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. */ { +setWg(); int baseWidth = seqEnd - seqStart; struct repeatItem *ri; int y = yOff; int heightPer = tg->heightPer; int lineHeight = tg->lineHeight; int x1, x2, w; Color col; struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr = NULL; char **row; int rowOffset; //Disabled //if (vis == tvFull || vis == tvSquish || vis == tvPack) if (vis == tvFull) @@ -1168,45 +1223,45 @@ } } 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) { +setWg(); 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; -// DEBUG REMOVE // If we are in full view mode and the scale is sufficient, // display the new visualization. //Disabled //if ((vis == tvFull || vis == tvSquish || vis == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE) if (vis == tvFull && 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++)