ded41ea1968e30e2c248f65d1c9fec9793cb82fc
max
  Thu Jun 18 11:31:32 2026 -0700
hgTracks: strand-direction arrows on the zoomed-in codon/CDS and UTR display

When a coding transcript is zoomed in far enough to color its codons, the
coding boxes carried no visible strand cue: only the introns showed the barbed
"fishbone" arrows, and the strand was otherwise visible only on mouseover.
This adds strand-direction chevrons to the codon and UTR display so the reading
direction is visible at a glance.

- At the amino-acid (codon-text) level, drawScaledBoxWithText() draws a chevron
at each codon's left boundary (in the whitespace between the centered letters),
in the box's contrasting color (white on the dark-blue codon shades), before
the letter so the letter stays crisp. Points right on + strand, left on -;
reverse-complement display is handled by hvGfxLine. The base-position ruler's
codon display passes strand 0 and is unaffected.

- When codons are colored but too small to label, baseColorDrawCdsArrows()
distributes white chevrons across the coding part of each exon, on top of the
boxes, at twice the standard barb spacing.

- The UTRs (which never showed arrows) now get chevrons too, in the feature's
contrasting color at a wider (3x) spacing as a cue that they are non-coding.
The chevron is sized to the short UTR box, with a 1px floor so it still shows
in squish mode.

refs #37779

diff --git src/hg/hgTracks/cds.h src/hg/hgTracks/cds.h
index 35159fbdd4d..a545e88067d 100644
--- src/hg/hgTracks/cds.h
+++ src/hg/hgTracks/cds.h
@@ -162,30 +162,40 @@
         int maxPixels, int winStart, Color originalColor);
 /*draw a box that is colored by the bases inside it and its
  * orientation. Stop codons are red, start are green, otherwise they
  * alternate light/dark blue colors. */
 
 void baseColorOverdrawDiff(struct track *tg,  struct linkedFeatures *lf,
 			   struct hvGfx *hvg, int xOff,
 			   int y, double scale, int heightPer,
 			   struct dnaSeq *qSeq, int qOffset, struct psl *psl,
 			   int winStart, enum baseColorDrawOpt drawOpt);
 /* If we're drawing different bases/codons, and zoomed out past base/codon 
  * level, draw 1-pixel wide red lines only where bases/codons differ from 
  * genomic.  This tests drawing mode and zoom level but assumes that lf itself 
  * has been drawn already and we're not in dense mode etc. */
 
+void baseColorDrawCdsArrows(struct track *tg, struct linkedFeatures *lf,
+                            struct hvGfx *hvg, int xOff, int y, double scale,
+                            int heightPer, int winStart, enum baseColorDrawOpt drawOpt,
+                            Color color);
+/* When zoomed in enough to color codons, distribute strand-direction chevrons
+ * across each exon on top of the boxes: white over the coding codons (only when
+ * they are too small to label), and the feature's contrasting color at a wider
+ * spacing over the UTRs as a cue that they are non-coding.  No-op below the
+ * codon-color zoom level, when coding coloring is off, or when strand unknown. */
+
 void baseColorOverdrawQInsert(struct track *tg,  struct linkedFeatures *lf,
 			      struct hvGfx *hvg, int xOff,
 			      int y, double scale, int heightPer,
 			      struct dnaSeq *qSeq, int qOffset, struct psl *psl,
 			      MgFont *font, int winStart, enum baseColorDrawOpt drawOpt,
 			      boolean indelShowQInsert, boolean indelShowPolyA);
 /* If applicable, draw 1-pixel wide orange lines for query insertions in the
  * middle of the query, 1-pixel wide purple lines for query insertions at the 
  * end of the query, and 1-pixel wide green (instead of purple) when a query 
  * insertion at the end is a valid poly-A tail. */
 
 
 struct simpleFeature *baseColorCodonsFromDna(int frame, int chromStart,
 					     int chromEnd, struct dnaSeq *seq,
 					     bool reverse);