33bb159f2fc8bc12bd68418424b0504dd1dd3bf8 braney Fri Nov 27 14:14:01 2020 -0800 fix the drawing of lollipops in multi-region diff --git src/hg/hgTracks/lollyTrack.c src/hg/hgTracks/lollyTrack.c index b0771dc..f0471da 100644 --- src/hg/hgTracks/lollyTrack.c +++ src/hg/hgTracks/lollyTrack.c @@ -30,97 +30,111 @@ }; static unsigned getLollyColor( struct hvGfx *hvg, unsigned color) /* Get the device color from our internal definition. */ { struct rgbColor itemRgb; itemRgb.r = (color & 0xff0000) >> 16; itemRgb.g = (color & 0xff00) >> 8; itemRgb.b = color & 0xff; return hvGfxFindColorIx(hvg, itemRgb.r, itemRgb.g, itemRgb.b); } void doYLabels(struct track *tg, struct hvGfx *hvg, int width, int height, struct lollyCartOptions *lollyCart, int xOff, int yOff, Color color, MgFont *font, boolean doLabels ) /* Draw labels or lines for labels. */ { -double range = lollyCart->upperLimit - lollyCart->lowerLimit; +double range = tg->graphUpperLimit - tg->graphLowerLimit; int fontHeight = tl.fontHeight+1; // we need a margin on top and bottom for half a lolly, and some space at the bottom // for the lolly stems double minimumStemHeight = fontHeight; double topAndBottomMargins = LOLLY_DIAMETER / 2 + LOLLY_DIAMETER / 2; double usableHeight = height - topAndBottomMargins - minimumStemHeight; yOff += LOLLY_DIAMETER / 2; struct hashEl *hel = trackDbSettingsLike(tg->tdb, "yAxisLabel*"); // parse lines like yAxisLabel <y offset> <draw line ?> <R,G,B> <label> for(; hel; hel = hel->next) { char *setting = cloneString((char *)hel->val); double number = atof(nextWord(&setting)); boolean drawLine = sameString("on", nextWord(&setting)); unsigned char red, green, blue; char *colorStr = nextWord(&setting); parseColor(colorStr, &red, &green, &blue); unsigned long lineColor = MAKECOLOR_32(red, green, blue); char *label = setting; - int offset = usableHeight * ((double)number - lollyCart->lowerLimit) / range; + int offset = usableHeight * ((double)number - tg->graphLowerLimit) / range; if (doLabels) hvGfxTextRight(hvg, xOff, yOff + (usableHeight - offset) - fontHeight / 2 , width - 1, fontHeight, color, font, label); else if (drawLine) hvGfxLine(hvg, xOff, yOff + (usableHeight - offset), xOff + width , yOff + (usableHeight - offset), lineColor); } } static void lollyDrawItems(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw a list of lolly structures. */ { double scale = scaleForWindow(width, seqStart, seqEnd); struct lolly *popList = tg->items, *pop; struct lollyCartOptions *lollyCart = tg->lollyCart; int trackHeight = tg->lollyCart->height ; -if (popList == NULL) // nothing to draw - return; - -if ( tg->visibility == tvDense) // in dense mode we just track lines +if ( tg->visibility == tvDense) // in dense mode we just draw lines { for (pop = popList; pop; pop = pop->next) { int sx = ((pop->start - seqStart) + .5) * scale + xOff; // x coord of center (lower region) unsigned color = getLollyColor(hvg, pop->color); hvGfxLine(hvg, sx, yOff, sx , yOff+ tl.fontHeight, color); } return; } doYLabels(tg, hvg, width, trackHeight, tg->lollyCart, xOff, yOff, color, font, FALSE); + +if (popList == NULL) // nothing to draw + return; + boolean noMapBoxes = FALSE; int numItems = slCount(popList); if ( numItems > 5000) { char buffer[4096]; safef(buffer, sizeof buffer, "Too many (%d) items in window. Zoom in or set viewing range to be more restrictive.", numItems); noMapBoxes = TRUE; mapBoxHgcOrHgGene(hvg, winStart, winEnd, xOff, yOff, width, trackHeight, tg->track, "", buffer, "hgTracks", FALSE, NULL); } -// first draw the lines so they won't overlap any lollies + +// calculate the lollipop heights +double range = tg->graphUpperLimit - tg->graphLowerLimit; int fontHeight = tl.fontHeight+1; double usableHeight = trackHeight - LOLLY_DIAMETER - fontHeight; +for(pop = popList; pop; pop = pop->next) + { + if (pop->radius == -1) + pop->radius = lollyCart->radius; + if (range == 0.0) + pop->height = usableHeight ; + else + pop->height = usableHeight * (pop->val - tg->graphLowerLimit) / range; + } + +// first draw the lines so they won't overlap any lollies yOff += LOLLY_DIAMETER / 2; if (!lollyCart->noStems) for (pop = popList; pop; pop = pop->next) { int sx = ((pop->start - seqStart) + .5) * scale + xOff; // x coord of center (lower region) hvGfxLine(hvg, sx, yOff + trackHeight, sx , yOff+(usableHeight - pop->height), MG_GRAY); } // now draw the sucker part! for (pop = popList; pop; pop = pop->next) { int sx = ((pop->start - seqStart) + .5) * scale + xOff; // x coord of center (lower region) unsigned color = getLollyColor(hvg, pop->color); hvGfxCircle(hvg, sx, yOff + (usableHeight - (pop->height )), pop->radius, color, TRUE); @@ -143,45 +157,45 @@ int centerLabel = (height/2)-(fontHeight/2); if ( tg->visibility == tvDense) { hvGfxTextRight(hvg, xOff, yOff+fontHeight, width - 1, fontHeight, color, font, tg->shortLabel); return; } //doYLabels(tg, hvg, width, height-fontHeight, lollyCart, xOff, yOff+fontHeight, color, font, TRUE); doYLabels(tg, hvg, width, height-fontHeight, lollyCart, xOff, yOff+fontHeight, color, font, TRUE); hvGfxText(hvg, xOff, yOff+centerLabel, color, font, tg->shortLabel); char *setting = trackDbSetting(tg->tdb, "yAxisNumLabels"); if (setting && sameString("off", setting)) return; -if (isnan(lollyCart->upperLimit)) +if (isnan(tg->graphUpperLimit)) { hvGfxTextRight(hvg, xOff, yOff + LOLLY_DIAMETER, width - 1, fontHeight, color, font, "NO DATA"); return; } char upper[1024]; -safef(upper, sizeof(upper), "%g -", lollyCart->upperLimit); +safef(upper, sizeof(upper), "%g -", tg->graphUpperLimit); hvGfxTextRight(hvg, xOff, yOff + fontHeight / 2 + LOLLY_DIAMETER / 2 , width - 1, fontHeight, color, font, upper); char lower[1024]; -if (lollyCart->lowerLimit < lollyCart->upperLimit) +if (tg->graphLowerLimit < tg->graphUpperLimit) { - safef(lower, sizeof(lower), "%g -", lollyCart->lowerLimit); + safef(lower, sizeof(lower), "%g -", tg->graphLowerLimit); hvGfxTextRight(hvg, xOff, yOff+height - LOLLY_DIAMETER / 2 - 2 *fontHeight + fontHeight/2 , width - 1, fontHeight, color, font, lower); } } static int lollyHeight(struct track *tg, enum trackVisibility vis) /* calculate height of all the lollys being displayed */ { if ( tg->visibility == tvDense) return tl.fontHeight; // return the height we calculated at load time return tg->lollyCart->height; } @@ -303,93 +317,118 @@ pop->mouseOver = restField(bb, mouseOverIdx); else if (mouseOverPattern) pop->mouseOver = replaceFieldInPattern(mouseOverPattern, bbi->fieldCount, fieldNames, bedRow); if (bbi->fieldCount > 8) pop->color = itemRgbColumn(bedRow[8]); count++; if (val > maxVal) maxVal = val; if (val < minVal) minVal = val; } if (filtered) labelTrackAsFilteredNumber(tg, filtered); -if (count == 0) - lollyCart->upperLimit = lollyCart->lowerLimit = NAN; // no lollies in range -else if (lollyCart->autoScale == wiggleScaleAuto) +if (lollyCart->autoScale == wiggleScaleAuto) { - lollyCart->upperLimit = maxVal; - lollyCart->lowerLimit = minVal; - } - -double range = lollyCart->upperLimit - lollyCart->lowerLimit; -int fontHeight = tl.fontHeight+1; -double usableHeight = trackHeight - LOLLY_DIAMETER - fontHeight; -for(pop = popList; pop; pop = pop->next) + if (maxVal == -DBL_MAX) { - if (pop->radius == -1) - pop->radius = lollyCart->radius; - if (range == 0.0) - pop->height = usableHeight ; + tg->graphUpperLimit = NAN; + tg->graphLowerLimit = NAN; + } else - pop->height = usableHeight * (pop->val - lollyCart->lowerLimit) / range; + { + tg->graphUpperLimit = maxVal; + tg->graphLowerLimit = minVal; + } } slSort(&popList, cmpHeight); tg->items = popList; } -static struct lollyCartOptions *lollyCartOptionsNew(struct cart *cart, struct trackDb *tdb, +static struct lollyCartOptions *lollyCartOptionsNew(struct cart *cart, struct track *track, int wordCount, char *words[]) // the structure that is attached to the track structure { +struct trackDb *tdb = track->tdb; struct lollyCartOptions *lollyCart; AllocVar(lollyCart); int maxHeightPixels; int minHeightPixels; int defaultHeight; /* pixels per item */ int settingsDefault; cartTdbFetchMinMaxPixels(cart, tdb, MIN_HEIGHT_PER, atoi(DEFAULT_HEIGHT_PER), atoi(DEFAULT_HEIGHT_PER), &minHeightPixels, &maxHeightPixels, &settingsDefault, &defaultHeight); lollyCart->origHeight = lollyCart->height = defaultHeight; lollyCart->autoScale = wigFetchAutoScaleWithCart(cart,tdb, tdb->track, NULL); double tDbMinY; /* data range limits from trackDb type line */ double tDbMaxY; /* data range limits from trackDb type line */ char *trackWords[8]; /* to parse the trackDb type line */ int trackWordCount = 0; /* to parse the trackDb type line */ wigFetchMinMaxYWithCart(cart, tdb, tdb->track, &lollyCart->minY, &lollyCart->maxY, &tDbMinY, &tDbMaxY, trackWordCount, trackWords); -lollyCart->upperLimit = lollyCart->maxY; -lollyCart->lowerLimit = lollyCart->minY; +track->graphUpperLimit = lollyCart->maxY; +track->graphLowerLimit = lollyCart->minY; lollyCart->radius = 5; lollyCart->noStems = trackDbSettingOn(tdb, "lollyNoStems"); char *setting = trackDbSetting(tdb, "lollyMaxSize"); if (setting) lollyCart->radius = atoi(setting); return lollyCart; } +void lollyRegionGraphLimits(struct track *tg) +/* Set common graphLimits across all windows */ +{ +double graphUpperLimit = -BIGDOUBLE; +double graphLowerLimit = BIGDOUBLE; +struct track *tgSave = tg; +struct window *w; + +// find graphLimits across all windows. +for(w=windows,tg=tgSave; w; w=w->next,tg=tg->nextWindow) + { + if (isnan(tg->graphUpperLimit)) + continue; + + if (tg->graphUpperLimit > graphUpperLimit) + graphUpperLimit = tg->graphUpperLimit; + if (tg->graphLowerLimit < graphLowerLimit) + graphLowerLimit = tg->graphLowerLimit; + } +if (graphUpperLimit == -BIGDOUBLE) + graphUpperLimit = graphLowerLimit = NAN; + +// set same common graphLimits in all windows. +for(w=windows,tg=tgSave; w; w=w->next,tg=tg->nextWindow) + { + tg->graphUpperLimit = graphUpperLimit; + tg->graphLowerLimit = graphLowerLimit; + } +} + void lollyMethods(struct track *track, struct trackDb *tdb, int wordCount, char *words[]) /* bigLolly track type methods */ { bigBedMethods(track, tdb, wordCount, words); -struct lollyCartOptions *lollyCart = lollyCartOptionsNew(cart, tdb, wordCount, words); +struct lollyCartOptions *lollyCart = lollyCartOptionsNew(cart, track, wordCount, words); lollyCart->typeWordCount = wordCount; AllocArray(lollyCart->typeWords, wordCount); int ii; for(ii=0; ii < wordCount; ii++) lollyCart->typeWords[ii] = cloneString(words[ii]); track->loadItems = lollyLoadItems; track->drawItems = lollyDrawItems; track->totalHeight = lollyHeight; +track->preDrawMultiRegion = lollyRegionGraphLimits; track->drawLeftLabels = lollyLeftLabels; track->lollyCart = lollyCart; }