6d437c28a5ff82a2af59ca516559053c4bf7c3f9 max Thu Jun 18 01:18:34 2026 -0700 hgTracks: amino-acid name in codon + ruler mouseovers; exon-length label; ruler "Complement bases" config toggle Three genome browser display changes around AA display: 1) amino acids shown on genePred codons and on rule codons, and making the "complement option" easier to find. 1) The genePred/bigGenePred codon mouseover (zoomed to the codon level) now shows the codon's amino acid on its own "Amino acid:" line, as the three-letter abbreviation plus full name (e.g. "Ala (alanine)"). Rather than reverse-decode the amino acid out of the codon's packed grayIx, the one-letter code is now stored on the codon when it is translated: struct simpleFeature gains a codonAa field, codonToGrayIx() reports the letter through a new out-param, and the codon mouseover reads simpleFeature.codonAa directly. The drawn codon letter and the mouseover then derive from the same translation and cannot drift apart, without the mouseover having to understand the grayIx encoding (the drawing path, colorAndCodonFromGrayIx(), still decodes its own grayIx inline, unchanged). Adds a one-letter->full-name aaToName() in lib/dnautil.c (using the previously unused name field of aminoAcidTable). Stop codons show "Ter (termination)". Also relabels the exon "Length" field as "Exon Length" in the codon and zoomed-out exon mouseovers; introns keep "Length". 2) The base-position ruler's three-reading-frame translation (hgt.baseShowCodons) now gives each codon box a mouse-over with the same three-letter abbreviation and full name, reading the stored codonAa via aaToName()/aaToAbbr() (baseColorDrawRulerCodons in cds.c). 3) Adds a "Complement the bases" checkbox to the base position (ruler) track configuration page (hgTrackUi rulerUi). It toggles the same per-assembly COMPLEMENT_BASES_VAR cart variable that was previously reachable only by clicking the "Click to complement" arrow next to the ruler. Also fixes the indentation of the adjacent drawComplementArrow() text assignments. refs #37779 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c index 6cfd1da99a2..8dcbc27bb98 100644 --- src/hg/hgTracks/simpleTracks.c +++ src/hg/hgTracks/simpleTracks.c @@ -3081,62 +3081,88 @@ { // 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 you change this text, make sure you also change hgTracks.js:mouseOverToLabel if (!isEmpty(existingText)) dyStringPrintf(codonDy, "<b>Transcript: </b> %s<br>", existingText); int codonHgvsIx = (codon->codonIndex - 1) * 3; if (codonHgvsIx >= 0) { int cStart = codonHgvsIx + 1; int cEnd = codonHgvsIx + 3; int pStart = codonHgvsIx / 3; + // the one-letter amino acid was stored on the codon when it + // was translated (cds.c); map it to its three-letter code + char aaLetter = codon->codonAa; + char aaAbbr[8]; + char *aaName = NULL; + if (aaLetter == '*') + { + safecpy(aaAbbr, sizeof(aaAbbr), "Ter"); + aaName = "termination"; + } + else if (aaLetter == 'X') // error/partial codon: nothing to show + aaAbbr[0] = '\0'; + else + { + aaToAbbr(aaLetter, aaAbbr, sizeof(aaAbbr)); + aaName = aaToName(aaLetter); + } dyStringPrintf(codonDy, "<b>Codons: </b> c.%d-%d (p.%d-%d)<br>", cStart, cEnd, pStart, pStart+1); + if (!isEmpty(aaAbbr)) + { + if (aaName != NULL) + dyStringPrintf(codonDy, "<b>Amino acid: </b> %s (%s)<br>", aaAbbr, aaName); + else + dyStringPrintf(codonDy, "<b>Amino acid: </b> %s<br>", aaAbbr); + } } // if you change the text below, also change hgTracks:mouseOverToExon - dyStringPrintf(codonDy, "<b>Strand: </b> %s <b>Length: </b>%dbp<br><b>Exon: </b>%s %d of %d<br>%s", + dyStringPrintf(codonDy, "<b>Strand: </b> %s <b>Exon Length: </b>%dbp<br><b>Exon: </b>%s %d of %d<br>%s", strandStr, e - s, exonIntronText, exonIntronNumber, numExonIntrons, phaseText); 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 // either an intron, or else an exon zoomed out too far for codons (or no codons) { // if you change this text, make sure you also change hgTracks.js:mouseOverToLabel // if you change the text below, also change hgTracks:mouseOverToExon char *posNote = ""; char *exonOrIntron = "Intron"; + char *lengthLabel = "Length:"; if (isExon) { posNote = "<b>Codons:</b> Zoom in to show cDNA position<br>"; exonOrIntron = "Exon"; + lengthLabel = "Exon Length:"; } safef(mouseOverText, sizeof(mouseOverText), "<b>Transcript:</b> %s<br>%s" - "<b>Strand:</b> %s<br><b>%s:</b> %s %d of %d <b>Length:</b> %d bp<br>%s", + "<b>Strand:</b> %s<br><b>%s:</b> %s %d of %d <b>%s</b> %d bp<br>%s", existingText, posNote, strandStr, exonOrIntron, exonIntronText, - exonIntronNumber, numExonIntrons, e - s, phaseText); + exonIntronNumber, numExonIntrons, lengthLabel, e - s, phaseText); // 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 old mouseOver lf->mouseOver = oldMouseOver; picStart = ex; // prevent pileups. is this right? add 1? does it work? // JC: Why do we care about pileups? First mapbox drawn wins. } } }