7bed3b764ff1081822f1a43845464107d71528e9 chmalee Mon Feb 26 09:25:48 2024 -0800 Add codon position information to genePred mouseovers, refs #32815 diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c index b67298a..722d19f 100644 --- src/hg/hgTracks/simpleTracks.c +++ src/hg/hgTracks/simpleTracks.c @@ -2833,70 +2833,71 @@ else { exonText = trackDbSettingClosestToHomeOrDefault(tg->tdb, "exonText" , "Exon" ); intronText = trackDbSettingClosestToHomeOrDefault(tg->tdb, "intronText", "Intron"); } while (exon != NULL) /* Make a stupid list of exons separate from what's given. */ /* It seems like lf->components isn't necessarily sorted. */ { refAdd(&exonList, exon); exon = exon->next; } /* Now sort it. */ slSort(&exonList, exonSlRefCmp); + numExons = slCount(exonList); struct genePred *gp = lf->original; boolean revStrand = (lf->orientation == -1); int eLast = -1; int s = -1; int e = -1; char mouseOverText[256]; boolean isExon = TRUE; int picStart = insideX; int picEnd = picStart + insideWidth; if (lButton) picStart += buttonW; if (rButton) picEnd -= buttonW; for (ref = exonList; TRUE; ) { exon = ref->val; if (isExon) { s = exon->start; e = exon->end; } else { s = eLast; e = exon->start; } // skip exons and introns that are completely outside the window - if (!(s > winEnd) || (e < winStart)) + if (s <= winEnd && e >= winStart) { int sClp = (s < winStart) ? winStart : s; int eClp = (e > winEnd) ? winEnd : e; int sx = round((sClp - winStart)*scale) + insideX; int ex = round((eClp - winStart)*scale) + insideX; // skip regions entirely outside available picture // (accounts for space taken by exon arrows buttons) - if (!(sx > picEnd) || (ex < picStart)) + if (sx <= picEnd && ex >= picStart) { // clip it to avail pic sx = (sx < picStart) ? picStart : sx; ex = (ex > picEnd) ? picEnd : ex; int w = ex - sx; int exonIntronNumber; char *exonIntronText; int numExonIntrons = numExons; if (isExon) { exonIntronText = exonText; } else @@ -2952,70 +2953,121 @@ if (startPhase==endPhase) exonNote = " → in-frame exon"; safef(buf, sizeof(buf), ", codon phase: start %d, end %d%s", startPhase, endPhase, exonNote); } else { if (startPhase==0) exonNote = " → in-frame exon"; safef(buf, sizeof(buf), ", start codon phase %d%s", startPhase, exonNote); } frameText = buf; } } + if (w > 0) // draw exon or intron if width is greater than 0 + { + // draw mapBoxes for the codons if we are zoomed in far enough + struct simpleFeature *codon; + struct dyString *codonDy = dyStringNew(0); + int codonS, codonE; + if (lf->codons && lf->codons->codonIndex > 0 && zoomedToCdsColorLevel) + { + for (codon = lf->codons; codon != NULL; codon = codon->next) + { + codonS = codon->start; codonE = codon->end; + if (codonS <= winEnd && codonE >= winStart) + { + int codonSClp = (codonS < winStart) ? winStart : codonS; + int codonEClp = (codonE > winEnd) ? winEnd : codonE; + + int codonsx = round((codonSClp - winStart)*scale) + insideX; + int codonex = round((codonEClp - winStart)*scale) + insideX; + + // skip regions entirely outside available picture + // (accounts for space taken by exon arrows buttons) + if (codonsx <= picEnd && codonex >= picStart) + { + // clip it to avail pic + codonsx = (codonsx < picStart) ? picStart : codonsx; + codonex = (codonex > picEnd) ? picEnd : codonex; + + int w = codonex - codonsx; + if (w > 0) + { + // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always + // prefer a lf->mouseOver over the itemName + char *oldMouseOver = lf->mouseOver; + lf->mouseOver = NULL; + dyStringClear(codonDy); + if (!isEmpty(existingText)) + dyStringPrintf(codonDy, "%s, ", existingText); + int codonHgvsIx = (codon->codonIndex - 1) * 3; + dyStringPrintf(codonDy, "c.%d-%d, ", codonHgvsIx + 1, codonHgvsIx + 3); + dyStringPrintf(codonDy, "strand %c, %s %d of %d%s", + strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText); + tg->mapItem(tg, hvg, item, codonDy->string, tg->mapItemName(tg, item), + sItem, eItem, codonsx, y, w, heightPer); + // and restore the mouseOver + lf->mouseOver = oldMouseOver; + } + } + } + } + } + else + { + // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always + // prefer a lf->mouseOver over the itemName if (!isEmpty(existingText)) safef(mouseOverText, sizeof(mouseOverText), "%s, strand %c, %s %d of %d%s", existingText, strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText); else safef(mouseOverText, sizeof(mouseOverText), "strand %c, %s %d of %d%s", strandChar, exonIntronText, exonIntronNumber, numExonIntrons, frameText); - - if (w > 0) // draw exon or intron if width is greater than 0 - { - // temporarily remove the mouseOver from the lf, since linkedFeatureMapItem will always - // prefer a lf->mouseOver over the itemName char *oldMouseOver = lf->mouseOver; lf->mouseOver = NULL; tg->mapItem(tg, hvg, item, mouseOverText, tg->mapItemName(tg, item), sItem, eItem, sx, y, w, heightPer); // and restore the mouseOver lf->mouseOver = oldMouseOver; + } picStart = ex; // prevent pileups. is this right? add 1? does it work? } } } if (isExon) { eLast = e; ref = ref->next; if (!ref) break; } else { exonIx++; } isExon = !isExon; if (s > winEnd) // since the rest will also be outside the window break; } + slFreeList(&exonList); } static struct window *makeMergedWindowList(struct window *windows) /* Make a copy of the windows list, merging nearby regions on the same chrom and which are within some limit (1MB?) of each other. */ { int mergeLimit = 1024*1024; struct window *w = windows; struct window *resultList = NULL; struct window *mergedW = NULL; if (!windows) errAbort("Unexpected error: windows list NULL in makeMergedWindowList()"); boolean doNew = TRUE; while(TRUE)