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/cds.c src/hg/hgTracks/cds.c index dc850b5..94009f9 100644 --- src/hg/hgTracks/cds.c +++ src/hg/hgTracks/cds.c @@ -44,43 +44,43 @@ Color lighterShade(struct hvGfx *hvg, Color color, double percentLess); /* Find a color which is a percentless 'lighter' shade of color */ /* Forward declaration */ /* Array of colors used in drawing codons/bases/differences: */ Color cdsColor[CDS_NUM_COLORS]; boolean cdsColorsMade = FALSE; Color getCdsColor(int index) { assert(index < CDS_NUM_COLORS); return cdsColor[index]; } -static void drawScaledBoxSampleWithText(struct hvGfx *hvg, +static void drawScaledBoxWithText(struct hvGfx *hvg, int chromStart, int chromEnd, double scale, int xOff, int y, int height, Color color, int score, MgFont *font, char *text, bool zoomed, int winStart, int maxPixels, boolean isCoding, boolean justifyString) /* Draw a box scaled from chromosome to window coordinates with a codon or set of 3 or less bases drawn in the box. */ { /*first draw the box itself*/ -drawScaledBoxSample(hvg, chromStart, chromEnd, scale, xOff, y, height, - color, score); +drawScaledBox(hvg, chromStart, chromEnd, scale, xOff, y, height, + color); /*draw text in box if space, and align properly for codons or DNA*/ if (zoomed) { int i; int x1, x2, w; x1 = round((double)(chromStart-winStart)*scale) + xOff; x2 = round((double)(chromEnd-winStart)*scale) + xOff; if (x2 >= maxPixels) x2 = maxPixels - 1; w = x2-x1; if (w < 1) w = 1; if (chromEnd - chromStart == 3 && isCoding) @@ -156,35 +156,91 @@ } } if (idx < 0) return(-1); if(psl->strand[1] == '-') thisQStart = psl->qSize - (qStarts[idx]+psl->blockSizes[idx]); else thisQStart = qStarts[idx]; return(thisQStart + (s - tStart)); } /* Calls to hDnaFromSeq are rather expensive for 2bit, so cache genomic sequence */ + +struct genoCacheWindow +{ +struct genoCacheWindow *next; +struct window *window; +char *initedTrack; +struct dnaSeq *cachedGenoDna; +int cachedGenoStart; +int cachedGenoEnd; +}; + +static struct genoCacheWindow *gcWindows = NULL; +static struct genoCacheWindow *gcWindow = NULL; +static struct genoCacheWindow *gcWindowOld = NULL; + +static bool setGcWindow() +/* scan genoCache windows. create new one if not found */ +{ +if (gcWindow && gcWindow->window == currentWindow) + return FALSE; +gcWindowOld = gcWindow; +for (gcWindow = gcWindows; gcWindow; gcWindow = gcWindow->next) + { + if (gcWindow->window == currentWindow) + { + return TRUE; + } + } +AllocVar(gcWindow); +gcWindow->window = currentWindow; +slAddTail(&gcWindows, gcWindow); +return TRUE; +} + + + static char *initedTrack = NULL; static struct dnaSeq *cachedGenoDna = NULL; static int cachedGenoStart = 0; static int cachedGenoEnd = 0; +static void setGc() +/* set up globals for the current window */ +{ +if (setGcWindow()) + { + if (gcWindowOld) + { + gcWindowOld->initedTrack = initedTrack; + gcWindowOld->cachedGenoDna = cachedGenoDna; + gcWindowOld->cachedGenoStart = cachedGenoStart; + gcWindowOld->cachedGenoEnd = cachedGenoEnd; + } + initedTrack = gcWindow->initedTrack; + cachedGenoDna = gcWindow->cachedGenoDna; + cachedGenoStart = gcWindow->cachedGenoStart; + cachedGenoEnd = gcWindow->cachedGenoEnd; + } + +} + static void getLinkedFeaturesSpan(struct linkedFeatures *lfList, int *retStart, int *retEnd, boolean isSeries) /* Find the overall lowest and highest coords in lfList. If any items hang off the * edge of the window, we will end up with coords winEnd which is * what we want. */ { int start = winStart, end = winEnd; if (isSeries) { struct linkedFeaturesSeries *lfs, *lfsList = (struct linkedFeaturesSeries *)lfList; for (lfs = lfsList; lfs != NULL; lfs = lfs->next) { if (lfs->start < start) start = lfs->start; if (lfs->end > end) @@ -200,30 +256,31 @@ start = lf->start; if (lf->end > end) end = lf->end; } } if (retStart) *retStart = start; if (retEnd) *retEnd = end; } static char *getCachedDna(int chromStart, int chromEnd) /* Return a pointer into our cached genomic dna. chromEnd is just for * bounds-checking (honor system). Do not change or free the return value! */ { +setGc(); if (!initedTrack || !cachedGenoDna) errAbort("getCachedDnaAt called before baseColorInitTrack?!"); if (chromStart < cachedGenoStart || chromEnd > cachedGenoEnd) errAbort("getCachedDnaAt: coords %d,%d are out of cached range %d,%d", chromStart, chromEnd, cachedGenoStart, cachedGenoEnd); return &(cachedGenoDna->dna[chromStart - cachedGenoStart]); } static void getNextCodonDna(char *retStr, int n, struct genePred *gp, int startI, boolean posStrand) /* Get at most n bases from coding exons following exon startI. */ { int i, j, thisN; int cdsExonStart, cdsExonEnd; char *codonDna; @@ -1373,87 +1430,87 @@ boolean isCoding = (drawOpt == baseColorDrawItemCodons || drawOpt == baseColorDrawDiffCodons); mrnaBases[0] = '\0'; if (psl && isCoding) getMrnaBases(psl, mrnaSeq, mrnaS, s, e, (lf->orientation == -1), mrnaBases, &queryInsertion); if (queryInsertion && isCoding) color = cdsColor[CDS_QUERY_INSERTION]; dyStringAppendN(dyMrnaSeq, (char*)&mrnaSeq->dna[mrnaS], e-s); if (drawOpt == baseColorDrawItemBases) { if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE)) complement(dyMrnaSeq->string, dyMrnaSeq->stringSize); - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, + drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, color, lf->score, font, dyMrnaSeq->string, zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE); } else if (drawOpt == baseColorDrawItemCodons) { if (e <= lf->tallEnd) { boolean startColor = FALSE; /* re-set color of this block based on mrna codons rather than * genomic, but keep the odd/even cycle of dark/light shades. */ int mrnaGrayIx = codonToGrayIx(mrnaBases, (grayIx > 26), NULL, FALSE, TRUE); if (color == cdsColor[CDS_START]) startColor = TRUE; color = colorAndCodonFromGrayIx(hvg, mrnaCodon, mrnaGrayIx, ixColor); if (startColor && sameString(mrnaCodon,"M")) color = cdsColor[CDS_START]; - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, + drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, color, lf->score, font, mrnaCodon, zoomedToCodonLevel, winStart, maxPixels, isCoding, TRUE); } else drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color); } else if (drawOpt == baseColorDrawDiffBases) { char *diffStr = NULL; char *genoDna = getCachedDna(s, e); diffStr = needMem(sizeof(char) * (e - s + 1)); maskDiffString(diffStr, dyMrnaSeq->string, genoDna, ' '); if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE)) complement(diffStr, strlen(diffStr)); - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, + drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, color, lf->score, font, diffStr, zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE); freeMem(diffStr); } else if (drawOpt == baseColorDrawDiffCodons) { if (e <= lf->tallEnd) { /* Color codons red wherever mrna differs from genomic; * keep the odd/even cycle of dark/light shades. */ colorAndCodonFromGrayIx(hvg, genomicCodon, grayIx, ixColor); int mrnaGrayIx = mrnaCodonToGrayIx(mrnaBases, genomicCodon[0], (grayIx > 26)); color = colorAndCodonFromGrayIx(hvg, mrnaCodon, mrnaGrayIx, ixColor); // Look up mrnaCodon again because if mrnaGrayIx is GRAYIX_SYN_PROT, // codon value is lost: safef(mrnaCodon, sizeof(mrnaCodon), "%c", baseColorLookupCodon(mrnaBases)); if (mrnaCodon[0] != genomicCodon[0]) { - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, + drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, color, lf->score, font, mrnaCodon, zoomedToCodonLevel, winStart, maxPixels, isCoding, TRUE); } else drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color); } else drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color); } else if (drawOpt != baseColorDrawCds) errAbort("Unknown drawOpt: %d
\n", drawOpt); dyStringFree(&dyMrnaSeq); } @@ -1479,77 +1536,77 @@ char codon[64] = " "; Color color = colorAndCodonFromGrayIx(hvg, codon, grayIx, originalColor); if (sf->codonIndex) safef(codon, sizeof(codon), "%c %d", codon[0], sf->codonIndex); /* When we are zoomed out far enough so that multiple bases/codons share the * same pixel, we have to draw differences in a separate pass (baseColorOverdrawDiff) * so don't waste time drawing the differences here: */ boolean zoomedOutToPostProcessing = ((drawOpt == baseColorDrawDiffBases && !zoomedToBaseLevel) || (drawOpt == baseColorDrawDiffCodons && !zoomedToCdsColorLevel)); if (drawOpt == baseColorDrawGenomicCodons && (e-s <= 3)) { if (lf->highlightColor) { - drawScaledBoxSample(hvg, s, e, scale, xOff, y, heightPer, - lf->highlightColor, lf->score ); - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y+1, heightPer-2, + drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, + lf->highlightColor); + drawScaledBoxWithText(hvg, s, e, scale, xOff, y+1, heightPer-2, color, lf->score, font, codon, zoomedToCodonLevel, winStart, maxPixels, TRUE, !sf->codonIndex); } else { - drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, + drawScaledBoxWithText(hvg, s, e, scale, xOff, y, heightPer, color, lf->score, font, codon, zoomedToCodonLevel, winStart, maxPixels, TRUE, !sf->codonIndex); } } else if (mrnaSeq != NULL && (psl != NULL || sf != NULL) && !zoomedOutToPostProcessing && drawOpt != baseColorDrawGenomicCodons && drawOpt != baseColorDrawOff) { if (lf->highlightColor) { - drawScaledBoxSample(hvg, s, e, scale, xOff, y, heightPer, - lf->highlightColor, lf->score ); + drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, + lf->highlightColor); drawDiffTextBox(hvg, xOff+1, y+1, scale, heightPer-2, font, color, chromName, s, e, sf, psl, mrnaSeq, lf, grayIx, drawOpt, maxPixels, tg->colorShades, originalColor); } else { drawDiffTextBox(hvg, xOff, y, scale, heightPer, font, color, chromName, s, e, sf, psl, mrnaSeq, lf, grayIx, drawOpt, maxPixels, tg->colorShades, originalColor); } } else { /* revert to normal coloring */ if (lf->highlightColor) { - drawScaledBoxSample(hvg, s, e, scale, xOff, y, heightPer, - lf->highlightColor, lf->score ); - drawScaledBoxSample(hvg, s, e, scale, xOff+1, y+1, heightPer -2, - color, lf->score ); + drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, + lf->highlightColor); + drawScaledBox(hvg, s, e, scale, xOff+1, y+1, heightPer -2, + color); } else { - drawScaledBoxSample(hvg, s, e, scale, xOff, y, heightPer, - color, lf->score ); + drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, + color); } } } static void drawCdsDiffCodonsOnly(struct track *tg, struct linkedFeatures *lf, struct hvGfx *hvg, int xOff, int y, double scale, int heightPer, struct dnaSeq *mrnaSeq, struct psl *psl, int winStart) /* Draw red boxes only where mRNA codons differ from genomic. This assumes * that lf has been drawn already, we're zoomed out past zoomedToCdsColorLevel, * we're not in dense mode etc. */ { struct simpleFeature *sf = NULL; @@ -1757,30 +1814,31 @@ (psl->tSize - (psl->tStarts[lastBlk] + psl->blockSizes[lastBlk])) : (psl->tStarts[lastBlk] + psl->blockSizes[lastBlk]); drawVertLine(lf, hvg, s, xOff, y, heightPer-1, scale, cdsColor[CDS_QUERY_INSERTION_AT_END]); } } } void baseColorInitTrack(struct hvGfx *hvg, struct track *tg) /* Set up base coloring state (e.g. cache genomic sequence) for tg. * This must be called by tg->drawItems if baseColorDrawSetup is used * in tg->drawItemAt. Peeks at tg->drawItems method to determine whether * tg is linkedFeatures or linkedFeaturesSeries (currently the only * two supported track types -- bed, psl etc. are subclasses of these). */ { +setGc(); if (initedTrack == NULL || differentString(tg->track, initedTrack)) { int overallStart, overallEnd; boolean isSeries = FALSE; #ifdef USE_BAM if (tg->drawItems == linkedFeaturesSeriesDraw || tg->drawItems == bamLinkedFeaturesSeriesDraw) #else if (tg->drawItems == linkedFeaturesSeriesDraw) #endif isSeries = TRUE; else if (!baseColorCanDraw(tg)) errAbort("baseColorInitTrack: track %s has a type not recognized by baseColorCanDraw.", tg->track); getLinkedFeaturesSpan((struct linkedFeatures *)tg->items, &overallStart, &overallEnd, @@ -1794,30 +1852,31 @@ } initedTrack = cloneString(tg->track); } /* allocate colors for coding coloring */ if (!cdsColorsMade) { makeCdsShades(hvg, cdsColor); cdsColorsMade = TRUE; } } static void checkTrackInited(struct track *tg, char *what) /* Die if baseColorInitTrack has not been called (most recently) for this track. */ { +setGc(); if (initedTrack == NULL || differentString(tg->track, initedTrack)) errAbort("Error: Track %s should have been baseColorInitTrack'd before %s. " "(tg->drawItems may be unrecognized by baseColorCanDraw)", tg->track, what); } enum baseColorDrawOpt baseColorDrawSetup(struct hvGfx *hvg, struct track *tg, struct linkedFeatures *lf, struct dnaSeq **retMrnaSeq, struct psl **retPsl) /* Returns the CDS coloring option, allocates colors if necessary, and * returns the sequence and psl record for the given item if applicable. * Note: even if base coloring is not enabled, this will return psl and * mrna seq if query insert/polyA coloring is enabled. * baseColorInitTrack must be called before this (in tg->drawItems) -- * this is meant to be called by tg->drawItemAt (i.e. linkedFeaturesDrawAt). */ @@ -1876,31 +1935,31 @@ of codons. Used for browser ruler in full mode*/ { struct simpleFeature *sf; if (!cdsColorsMade) { makeCdsShades(hvg, cdsColor); cdsColorsMade = TRUE; } for (sf = sfList; sf != NULL; sf = sf->next) { char codon[4]; Color color = colorAndCodonFromGrayIx(hvg, codon, sf->grayIx, MG_GRAY); if (zoomedToText) - drawScaledBoxSampleWithText(hvg, sf->start, sf->end, scale, insideX, y, + drawScaledBoxWithText(hvg, sf->start, sf->end, scale, insideX, y, height, color, 1.0, font, codon, TRUE, winStart, maxPixels, TRUE, TRUE); else /* zoomed in just enough to see colored boxes */ drawScaledBox(hvg, sf->start, sf->end, scale, xOff, y, height, color); } } void baseColorDrawCleanup(struct linkedFeatures *lf, struct dnaSeq **pMrnaSeq, struct psl **pPsl) /* Free structures allocated just for base/cds coloring. */ { // We could free lf->original here (either genePredFree or pslFree, depending // on the type -- but save time by skipping that. Maybe we should save time