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;