dfb8e59201a2b43e28b6db8bf04c020f1bce6ac6
markd
  Fri Aug 5 17:22:19 2022 -0700
fix bigRmsk label being cut off in full mode (#29845)

diff --git src/hg/hgTracks/bigRmskTrack.c src/hg/hgTracks/bigRmskTrack.c
index 449bf3b..2ca2623 100644
--- src/hg/hgTracks/bigRmskTrack.c
+++ src/hg/hgTracks/bigRmskTrack.c
@@ -20,30 +20,31 @@
 #include "jksql.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "bigRmskUi.h"
 
 /* Track Development Notes:
  *
  * Summary of concerns:
  *      1. tg->loadItems:
  *            Should be responsible for loading data "items".
  *            In our case they will be bigRmskRecords.
  *      2. tg->totalHeight:
  *            Should be responsible for layout calculations
  *            that determine the totalHeight of the track at the
  *            current scale and/or visual style ( 'pack', 'full' etc. )
+ *            NOTE: This doesn't include the track center label (if present)
  *      3. tg->preDrawItems:
  *            Not sure the use case for this. 
  *      4. tg->drawItems:
  *            Pretty self explanatory.
  *      5. tg->drawLeftLabels:
  *            Draw labels to left of track (if desired).
  *
  * Detailed track flow:
  *      mainMain:
  *        cartHtmlShell ... doMiddle()
  *        
  *      hgTrack:
  *        doMiddle() ### called by mainMain ###
  *            tracksDisplay()
  *                doTrackForm() 
@@ -652,76 +653,78 @@
       //Could also...have DB loader here in the future.
     tg->items = detailList;
     // Man, I went back and forth on this one.  I *wish* there was
     // an API for layout in between the loadItems() and totalHeight()
     // stubs. The totalHeight() method is called multiple times which
     // isn't ideal for layout operations since the layout logic needs
     // to detect if it needs to really re-layout the data (expensive).
     // For now, this is the only way to be assured that it gets called
     // once for real changes in the visual appearance.
     bigRmskLayoutItems(tg);
 }
   
 
 int bigRmskItemHeight(struct track *tg, void *item)
 {
-if (tg->limitedVis == tvDense) 
+enum trackVisibility vis = tg->visibility;
+if (vis == tvDense) 
     tg->heightPer = tl.fontHeight;
-else if (tg->limitedVis == tvPack)
+else if (vis == tvPack)
     tg->heightPer = tl.fontHeight;
-else if (tg->limitedVis == tvSquish)
+else if (vis == tvSquish)
     tg->heightPer = tl.fontHeight/2;
-else if (tg->limitedVis == tvFull )
+else if (vis == tvFull )
     {
     if ( winBaseCount <= DETAIL_VIEW_MAX_SCALE)
         tg->heightPer = max(tl.fontHeight, MINHEIGHT);
     else 
         tg->heightPer = tl.fontHeight;
     }
 tg->lineHeight = tg->heightPer + 1;
 
 return(tg->heightPer);
 }
 
 
 int bigRmskTotalHeight(struct track *tg, enum trackVisibility vis)
 {
 boolean origPackViz = cartUsualBoolean(cart, BIGRMSK_ORIG_PACKVIZ, BIGRMSK_ORIG_PACKVIZ_DEFAULT);
 
+// Ensure that lineHeight is set correctly
 bigRmskItemHeight(tg, (void *)NULL);
 
 int count = slCount(tg->items);
 if ( count > MAX_PACK_ITEMS || vis == tvDense ) 
     {
     tg->height = tg->lineHeight;
     return(tg->height);
     }
 else
     {
     // Count the layout depth 
     struct bigRmskRecord *cr;
     int numLevels = 0;
     for ( cr = tg->items; cr != NULL; cr = cr->next )
         {
         if ( cr->layoutLevel > numLevels )
             numLevels = cr->layoutLevel;
         }
     numLevels++;
 
     if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
         {
-        tg->height = numLevels * max(tg->heightPer, MINHEIGHT);
+        tg->height = numLevels * max(tg->lineHeight, MINHEIGHT);
         }
     else 
         {
         if ( origPackViz ) 
             {
             // Original class-per-line track
             int numClasses = ArraySize(rptClasses);
             tg->height = numClasses * tg->lineHeight;
             }
         else
             {
             // Color pack track
             tg->height = numLevels * tg->lineHeight;
             }
         }
@@ -1113,59 +1116,60 @@
     else
     {
     cx1 += gapLen;
     }
 
     if (cx1 > x2)
         break;
     }
 }
 
 
 static void drawNormalBox(struct hvGfx *hvg, int x1, int y1,
            int width, int height, Color fillColor, Color outlineColor)
 {
 struct gfxPoly *poly = gfxPolyNew();
-int y2 = y1 + height;
-int x2 = x1 + width;
+// RMH: Fixed 8/4/22 : One-off error in height/width to x/y calcs
+int y2 = y1 + height - 1;
+int x2 = x1 + width - 1;
   /*
    *     1,5--------------2
    *       |              |
    *       |              |
    *       |              |
    *       4--------------3
    */
 gfxPolyAddPoint(poly, x1, y1);
 gfxPolyAddPoint(poly, x2, y1);
 gfxPolyAddPoint(poly, x2, y2);
 gfxPolyAddPoint(poly, x1, y2);
 gfxPolyAddPoint(poly, x1, y1);
 hvGfxDrawPoly(hvg, poly, fillColor, TRUE);
 hvGfxDrawPoly(hvg, poly, outlineColor, FALSE);
 gfxPolyFree(&poly);
 }
 
 
 static void drawBoxWChevrons(struct hvGfx *hvg, int x1, int y1,
           int width, int height, Color fillColor, Color outlineColor,
           char strand)
 {
 drawNormalBox(hvg, x1, y1, width, height, fillColor, outlineColor);
 static Color white = 0xffffffff;
 int dir = (strand == '+' ? 1 : -1);
 int midY = y1 + ((height) >> 1);
-clippedBarbs(hvg, x1 + 1, midY, width, ((height >> 1) - 2), 5,
+clippedBarbs(hvg, x1 + 1, midY, width - 2, ((height >> 1) - 2), 5,
           dir, white, TRUE);
 }
 
 
 static void drawRMGlyph(struct hvGfx *hvg, int y, int heightPer,
         int width, int baseWidth, int xOff, struct bigRmskRecord *rm,
             enum trackVisibility vis, boolean showUnalignedExtents, boolean showLabels)
 /*
  *  Draw a detailed RepeatMasker annotation glyph given
  *  a single bigRmskRecord structure.
  *
  *  A couple of caveats about the use of the bigRmskRecord
  *  structure to hold a RepeatMasker annotation.
  *
  *  chromStart/chromEnd:  These represent genomic coordinates
@@ -1203,30 +1207,32 @@
  *                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
  *
  */
 {
 int idx;
 int lx1, lx2;
 int cx1, cx2;
+int end;
+char lenLabel[20];
 int w;
 struct classRecord *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;
 float pixelsPerBase = (float)width / (float)baseWidth;
 
 Color black = hvGfxFindColorIx(hvg, 0, 0, 0);
 Color fillColor = shadesOfGray[5];
 Color outlineColor = rmskJoinedClassColors[0];
 
 // Break apart the name and get the class of the
@@ -1407,53 +1413,51 @@
 
             if ( showLabels ) 
                 {
                 MgFont *font = tl.font;
                 int fontHeight = tl.fontHeight;
                 int stringWidth =
                      mgFontStringWidth(font, rm->name) + LABEL_PADDING;
 
                 if ( lx1-stringWidth-xOff > 0 ) 
                     {
                     hvGfxTextCentered(hvg, lx1 - stringWidth,
                               heightPer - fontHeight + y,
                               stringWidth, fontHeight, MG_BLACK, font,
                               rm->name);
                     rm->leftLabel = 0;
-                    }
-                else
+                    }else
                     { 
-                    int end = rm->alignEnd;
+                    end = rm->alignEnd;
 
                     if ( showUnalignedExtents )
                         {
                         if ((rm->blockSizes[rm->blockCount - 1] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN)
                             {
-                            char lenLabel[20];
                             safef(lenLabel, sizeof(lenLabel), "%d",
                                   rm->blockSizes[rm->blockCount - 1]);
                             end += (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
                                    (int) ((mgFontStringWidth(tl.font, lenLabel) +
                                    LABEL_PADDING) / pixelsPerBase);
                             }
                         else
                             {
                             end += rm->blockSizes[rm->blockCount - 1];
                             }
                         }
 
-                    int endx = roundingScale(rm->chromEnd - winStart,
+                    int endx = roundingScale(rm->visualEnd - winStart,
                                         width, baseWidth);
                     if ( endx > 0 ) 
                         rm->leftLabel = 1;
                     }
                 }
             }
         else if (idx == (rm->blockCount - 1))
             {
             if ( showUnalignedExtents )
                 {
                 /*
                  * Unaligned sequence at the end of an annotation
                  * Draw as:
                  *       -------------|   or        ------//------|
                  *       |                          |
@@ -1659,31 +1663,31 @@
  * int heightPer = tg->heightPer;
  */
 int heightPer = bigRmskItemHeight(tg, NULL);
 struct bigRmskRecord *rm;
 boolean showUnalignedExtents = cartUsualBoolean(cart, BIGRMSK_SHOW_UNALIGNED_EXTENTS, BIGRMSK_SHOW_UNALIGNED_EXTENTS_DEFAULT);
 boolean showLabels = cartUsualBoolean(cart, BIGRMSK_SHOW_LABELS, BIGRMSK_SHOW_LABELS_DEFAULT);
 boolean origPackViz = cartUsualBoolean(cart, BIGRMSK_ORIG_PACKVIZ, BIGRMSK_ORIG_PACKVIZ_DEFAULT);
 
 // If we are in full view mode and the scale is sufficient,
 // display the detailed visualization.
 if (vis == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     int level;
     for (rm = tg->items; rm != NULL; rm = rm->next)
         {
-        level = yOff + (rm->layoutLevel * heightPer);
+        level = yOff + (rm->layoutLevel * tg->lineHeight);
         drawRMGlyph(hvg, level, heightPer, width, baseWidth, 
                     xOff, rm, vis, showUnalignedExtents, showLabels );
         char statusLine[128];
         int ss1 = roundingScale(rm->alignStart - winStart,
                                 width, baseWidth) + xOff;
 
         safef(statusLine, sizeof(statusLine), "%s", rm->name);
 
         int x1 = roundingScale(rm->alignStart - winStart, width,
                             baseWidth) + xOff;
         x1 = max(x1, 0);
         int x2 = roundingScale(rm->alignEnd - winStart, width,
                             baseWidth) + xOff;
         int w = x2 - x1;
         if (w <= 0)
@@ -1718,76 +1722,81 @@
             drawPackGlyphs(tg, seqStart, seqEnd,
                                 hvg,  xOff, yOff, width,
                                 font, color,  vis);
         }
     }
 }
 
 
 void bigRmskLeftLabels(struct track *tg, int seqStart, int seqEnd,
         struct hvGfx *hvg, int xOff, int yOff, int width, int height,
         boolean withCenterLabels, MgFont *font, Color color,
         enum trackVisibility vis)
 /* draw the labels on the left margin */
 {
 boolean origPackViz = cartUsualBoolean(cart, BIGRMSK_ORIG_PACKVIZ, BIGRMSK_ORIG_PACKVIZ_DEFAULT);
-int y;
 Color labelColor = tg->labelColor;
 labelColor = maybeDarkerLabels(tg, hvg, labelColor);
-int fontHeight = tl.fontHeight+1;
 int heightPer = bigRmskItemHeight(tg, NULL);
-y = yOff + fontHeight;
+int titleFontHeight = tl.fontHeight;
+int labelFontHeight = mgFontLineHeight(font);
+int yOffAfterTitle = yOff + titleFontHeight;
+int fontOffset = heightPer - labelFontHeight;
+int y = yOffAfterTitle;
 
 if (vis == tvDense)
     {
     hvGfxTextRight(hvg, xOff, y, width - 1,
                    heightPer, labelColor, font, "Repeats");
     }
 else if ( vis == tvPack && origPackViz ) 
     {
     int i;
     int numClasses = ArraySize(rptClasses);
     for (i = 0; i < numClasses; ++i)
         {
-        hvGfxTextRight(hvg, xOff, y, width - 1,
+        hvGfxTextRight(hvg, xOff, y + fontOffset, width - 1,
                        heightPer, labelColor, font, rptClassNames[i]);
         y += heightPer;
         }
      }
 else 
     {
     struct bigRmskRecord *cr;
+int highestLevel = 0;
     for (cr = tg->items; cr != NULL; cr = cr->next)
         {
+if ( cr->layoutLevel > highestLevel )
+  highestLevel = cr->layoutLevel;
         if ( cr->leftLabel ) 
             {
             // Break apart the name and get the class of the
             // repeat.
             char family[256];
             // Simplify repClass for lookup: strip trailing '?',
             // simplify *RNA to RNA:
             char *poundPtr = index(cr->name, '#');
             safecpy(family, sizeof(family), cr->name);
             if (poundPtr)
                 {
                 poundPtr = index(family, '#');
                 *poundPtr = '\0';
                 }
-            y = yOff + ((cr->layoutLevel+1) * tg->lineHeight);
+            y = yOffAfterTitle + (cr->layoutLevel * tg->lineHeight) + fontOffset;
             hvGfxSetClip(hvgSide, leftLabelX, y, insideWidth, tg->height);
-            hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, tg->lineHeight,
-                           color, font, family);
+            hvGfxTextRight(hvgSide, leftLabelX, y, leftLabelWidth-1, 
+                           heightPer-fontOffset, color, font, family);
             hvGfxUnclip(hvgSide);
             }
         }
     }
 }
 
 
 void bigRmskMethods(struct track *track, struct trackDb *tdb,
                   int wordCount, char *words[])
 {
 bigBedMethods(track, tdb, wordCount, words);
 track->loadItems = bigRmskLoadItems;
 track->totalHeight = bigRmskTotalHeight;
 track->drawItems = bigRmskDrawItems;
 track->drawLeftLabels = bigRmskLeftLabels;