f30e46e4d2ee9f48ef87cc87b95159c37dffeb6a galt Wed Feb 17 12:59:44 2016 -0800 Fixes #16818. Gets better cross-all-windows error handling working for bigDataUrl/network tracks. Squashed commit of the following: commit cdf9db633c9018538aabf9c8346eabee63b5a3cf Author: Galt Barber Date: Wed Feb 17 12:55:47 2016 -0800 Fixed width in bigWarn.c and background in multiWig.c commit 3620dcd0912660fabbd993df4af119cc03676545 Author: Galt Barber Date: Tue Feb 16 11:53:32 2016 -0800 Initial checking for multi-region all-windows warn. This is an attempt to handle timeout high-level errors better, when a track is taking too long to finish loading; and to handle the network errors across all of the windows together, i.e. using the entire screen width to display the errors, only diplaying the first error, and only drawing for the first window. diff --git src/hg/hgTracks/multiWig.c src/hg/hgTracks/multiWig.c index 7c05cff..534a6a4 100644 --- src/hg/hgTracks/multiWig.c +++ src/hg/hgTracks/multiWig.c @@ -1,578 +1,585 @@ /* A container for multiple wiggles with a couple of options for combining them. */ /* Copyright (C) 2014 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "hash.h" #include "linefile.h" #include "jksql.h" #include "dystring.h" #include "hdb.h" #include "hgTracks.h" #include "container.h" #include "wiggle.h" #include "wigCommon.h" #include "hui.h" struct floatPic /* A picture that stores RGB values in floating point. */ { float **lines; /* points to start of each line of image */ float *image; int width; /* Width in pixels. */ int height; /* Heigh in pixels. */ }; struct floatPic *floatPicNew(int width, int height) /* Return a new floatPic. */ { long lineSize = 3L * width; long imageSize = lineSize * height; struct floatPic *pic = needMem(sizeof(struct floatPic)); pic->width = width; pic->height = height; pic->image = needHugeMem(imageSize * sizeof(float)); /* Create and initialize line start array */ AllocArray(pic->lines, height); int i = height; float *line = pic->image; float **lines = pic->lines; while (--i >= 0) { *lines++ = line; line += lineSize; } return pic; } void floatPicFree(struct floatPic **pPic) /* Free up resources associated with floatPic. */ { struct floatPic *pic = *pPic; if (pic != NULL) { freeMem(pic->lines); freeMem(pic->image); freez(pPic); } } void floatPicSet(struct floatPic *pic, float r, float g, float b) /* Set full image to a single color */ { long totalSize = pic->width * pic->height; float *p = pic->image; while (--totalSize >= 0) { *p++ = r; *p++ = g; *p++ = b; } } // The value below is useful when scaling between floating point 0-1 representation // and 0-255 fit-in-a-byte representation. If you use 256, then a valid 1.0 wraps // to an invalid 256 value in the byte. If you use 255 then even 0.999999 maps // to 254, which is a waste of space. #define FLOAT_FOR_BIGGEST_BYTE 255.9 void vLineViaFloat(void *image, int x, int y, int height, Color color) /* A vertical line drawer that works via floatPic. */ { struct floatPic *pic = image; /* First do some clipping */ if (x < 0 || x > pic->width) return; if (y < 0) { height += y; y = 0; } int yEnd = y + height; if (yEnd > pic->height) { yEnd = pic->height; height = yEnd - y; } if (height <= 0) return; int hOffset = x*3; const float scaleColor = 1/FLOAT_FOR_BIGGEST_BYTE; float r = COLOR_32_RED(color) * scaleColor; float g = COLOR_32_GREEN(color) * scaleColor; float b = COLOR_32_BLUE(color) * scaleColor; while (--height >= 0) { float *p = pic->lines[y++] + hOffset; p[0] *= r; p[1] *= g; p[2] *= b; } } struct wigGraphOutput *wigGraphOutputTransparent(struct floatPic *image) /* Get appropriate wigGraphOutput for non-transparent rendering */ { struct wigGraphOutput *wgo; AllocVar(wgo); wgo->image = image; wgo->vLine = vLineViaFloat; return wgo; } static void reverseLineOfColors(Color *line, int length) /* Reverse order of colors in line. */ { long halfLen = (length>>1); Color *end = line+length; Color c; while (--halfLen >= 0) { c = *line; *line++ = *--end; *end = c; } } void floatPicIntoHvg(struct floatPic *pic, int xOff, int yOff, struct hvGfx *hvg) /* Copy float pic into hvg image at given offset. */ { int width = pic->width, height = pic->height; Color *lineBuf; AllocArray(lineBuf, width); int y; for (y=0; ylines[y]; Color *cp = lineBuf; int i = width; while (--i >= 0) { int red = fp[0]*FLOAT_FOR_BIGGEST_BYTE; int green = fp[1]*FLOAT_FOR_BIGGEST_BYTE; int blue = fp[2]*FLOAT_FOR_BIGGEST_BYTE; *cp++ = MAKECOLOR_32(red, green, blue); fp += 3; } if (hvg->rc) reverseLineOfColors(lineBuf, width); hvGfxVerticalSmear(hvg, xOff, y + yOff, width, 1, lineBuf, TRUE); } freez(&lineBuf); } static void minMaxVals(struct slRef *refList, double *retMin, double *retMax, enum wiggleWindowingEnum windowingFunction,enum wiggleAlwaysZeroEnum alwaysZero, double *yOffsets) /* Figure out min/max of everything in list. The refList contains pointers to * preDrawContainers. Calculates the yOffsets used to make the stacked-graph overlay mode. */ { /* Turns out to be *much* shorter to rewrite than to reuse preDrawAutoScale */ double max = -BIGDOUBLE, min = BIGDOUBLE; struct slRef *ref; int numTrack = 0; for (ref = refList; ref != NULL; numTrack++,ref = ref->next) { struct preDrawContainer *pre = ref->val; if (pre == NULL) // pre may be null if the bigWig file didn't load continue; struct preDrawElement *p = pre->preDraw + pre->preDrawZero; int width = pre->width; int i; int offset = numTrack * pre->width; int prevOffset = (numTrack - 1) * pre->width; for (i=0; ismooth; if (p->count) { if (yOffsets) yOffsets[offset] = val; else { if (min > val) min = val; if (max < val) max = val; } } else if (yOffsets) { yOffsets[offset] = 0; } if (yOffsets && (numTrack > 0)) { yOffsets[offset] += yOffsets[prevOffset]; if (min > yOffsets[offset]) min = yOffsets[offset]; if (max < yOffsets[offset]) max = yOffsets[offset]; } ++p; } } if (alwaysZero == wiggleAlwaysZeroOn) { if ( max < 0) max = 0.0; else if ( min > 0) min = 0.0; } *retMax = max; *retMin = min; } static struct wigGraphOutput *setUpWgo(int xOff, int yOff, int width, int height, int numTracks, struct wigCartOptions *wigCart, struct hvGfx *hvg) { /* Deal with tranparency possibly */ struct wigGraphOutput *wgo; struct floatPic *floatPic = NULL; switch(wigCart->aggregateFunction) { case wiggleAggregateTransparent: { //int height = tg->lineHeight; floatPic = floatPicNew(width, height); floatPicSet(floatPic, 1, 1, 1); wgo = wigGraphOutputTransparent(floatPic); break; } case wiggleAggregateNone: case wiggleAggregateSolid: { wgo = wigGraphOutputSolid(xOff, yOff, hvg); break; } case wiggleAggregateStacked: { wgo = wigGraphOutputStack(xOff, yOff, width, numTracks, hvg); break; } default: { errAbort("bad aggregate function (value: %d)\n", wigCart->aggregateFunction); break; } } return wgo; } static void multiWigPreDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Pre-Draw multiWig container calls preDraw on all subtracks. */ { int y = yOff; // The y value here should not matter. struct track *subtrack; struct wigCartOptions *wigCart = tg->wigCartData; // we want to the order to be the same in all the modes if (wigCart->aggregateFunction == wiggleAggregateStacked) slReverse(&tg->subtracks); int numTracks = 0; // determine if any subtracks had errors and count them up for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack) ) { numTracks++; } } struct wigGraphOutput *wgo = setUpWgo(xOff, yOff, width, tg->height, numTracks, wigCart, hvg); tg->wigGraphOutput = wgo; // Cope with autoScale and stacked bars - we do it here rather than in the child tracks, so that // all children can be on same scale. double minVal, maxVal; // Force load of all predraw arrays so can do calcs. Build up list, and then // figure out max/min. No worries about multiple loading, the loaders protect // themselves. struct slRef *refList = NULL; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { struct preDrawContainer *pre = subtrack->loadPreDraw(subtrack, seqStart, seqEnd, width); if (pre != NULL) // pre maybe null if the load fails { pre->skipAutoscale = TRUE; subtrack->preDrawItems(subtrack, seqStart, seqEnd, hvg, xOff, y, width, font, color, vis); refAdd(&refList, pre); } } } slReverse(&refList); minMaxVals(refList, &minVal, &maxVal, wigCart->windowingFunction, wigCart->alwaysZero, wgo->yOffsets); slFreeList(&refList); if (!wigCart->autoScale) { minVal = wigCart->minY; maxVal = wigCart->maxY; } // Loop through again setting up the wigCarts of the children to have minY/maxY for // our limits and autoScale off. for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { struct wigCartOptions *wigCart = subtrack->wigCartData; wigCart->minY = minVal; wigCart->maxY = maxVal; wigCart->autoScale = wiggleScaleManual; struct preDrawContainer *pre = subtrack->preDrawContainer; if (pre != NULL) // pre maybe null if the load fails { pre->graphUpperLimit = maxVal; pre->graphLowerLimit = minVal; } } } } static void multiWigMultiRegionGraphLimits(struct track *tg) /* Set graphLimits for subtracks. */ { struct track *subtrack; double graphUpperLimit = -BIGDOUBLE; double graphLowerLimit = BIGDOUBLE; struct window *w; struct track *tgs; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { // find common graphLimits across all windows. for(w=windows,tgs=subtrack; w; w=w->next,tgs=tgs->nextWindow) { struct preDrawContainer *pre = tgs->preDrawContainer; if (pre != NULL) // pre maybe null if the load fails { if (pre->graphUpperLimit > graphUpperLimit) graphUpperLimit = pre->graphUpperLimit; if (pre->graphLowerLimit < graphLowerLimit) graphLowerLimit = pre->graphLowerLimit; } } break; // can exit since the rest of the subtracks have been set to the same limits. } } // set same common graphLimits across all subtracks. for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { // set same common graphLimits across all windows. for(w=windows,tgs=subtrack; w; w=w->next,tgs=tgs->nextWindow) { struct wigCartOptions *wigCart = tgs->wigCartData; wigCart->minY = graphUpperLimit; wigCart->maxY = graphLowerLimit; struct preDrawContainer *pre = tgs->preDrawContainer; if (pre != NULL) // pre maybe null if the load fails { pre->graphUpperLimit = graphUpperLimit; pre->graphLowerLimit = graphLowerLimit; } tgs->graphUpperLimit = graphUpperLimit; tgs->graphLowerLimit = graphLowerLimit; } } } } static void multiWigDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw items in multiWig container. */ { struct track *subtrack; boolean errMsgShown = FALSE; int y = yOff; boolean errMsgFound = FALSE; // determine if any subtracks had errors and count them up for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack) ) { if (subtrack->networkErrMsg) errMsgFound = TRUE; } } -if (errMsgFound) +if (errMsgFound && currentWindow==windows) // first window { + int clipXBak, clipYBak, clipWidthBak, clipHeightBak; + hvGfxGetClip(hvg, &clipXBak, &clipYBak, &clipWidthBak, &clipHeightBak); + hvGfxUnclip(hvg); + hvGfxSetClip(hvg, fullInsideX, yOff, fullInsideWidth, tg->height); + // use the height of the multiWig Color yellow = hvGfxFindRgb(hvg, &undefinedYellowColor); - hvGfxBox(hvg, xOff, yOff, width, tg->height, yellow); + hvGfxBox(hvg, fullInsideX, yOff, fullInsideWidth, tg->height, yellow); + hvGfxUnclip(hvg); + hvGfxSetClip(hvg, clipXBak, clipYBak, clipWidthBak, clipHeightBak); } struct wigCartOptions *wigCart = tg->wigCartData; struct wigGraphOutput *wgo = tg->wigGraphOutput; int numTrack = 0; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { if (!subtrack->networkErrMsg || !errMsgShown) { if (subtrack->networkErrMsg) errMsgShown = TRUE; wgo->numTrack = numTrack++; subtrack->wigGraphOutput = wgo; int height = subtrack->totalHeight(subtrack, vis); hvGfxSetClip(hvg, xOff, y, width, height); if (wigCart->aggregateFunction != wiggleAggregateNone) subtrack->lineHeight = tg->lineHeight; subtrack->drawItems(subtrack, seqStart, seqEnd, hvg, xOff, y, width, font, color, vis); if (wigCart->aggregateFunction == wiggleAggregateNone) { y += height + 1; wgo->yOff = y; } hvGfxUnclip(hvg); } } } if (wigCart->aggregateFunction == wiggleAggregateTransparent) { floatPicIntoHvg(wgo->image, xOff, yOff, hvg); floatPicFree((struct floatPic **)&wgo->image); } char *url = trackUrl(tg->track, chromName); mapBoxHgcOrHgGene(hvg, seqStart, seqEnd, xOff, yOff, width, tg->height, tg->track, tg->track, NULL, url, TRUE, NULL); } static int multiWigTotalHeight(struct track *tg, enum trackVisibility vis) /* Return total height of multiWigcontainer. */ { struct wigCartOptions *wigCart = tg->wigCartData; int totalHeight = 0; if (wigCart->aggregateFunction != wiggleAggregateNone) totalHeight = wigTotalHeight(tg, vis); struct track *subtrack; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { // Logic is slightly complicated by fact we want to call the totalHeight // method for each subtrack even if in overlay mode. int oneHeight = subtrack->totalHeight(subtrack, vis); if (wigCart->aggregateFunction == wiggleAggregateNone) { if (totalHeight != 0) totalHeight += 1; totalHeight += oneHeight; } } } tg->height = totalHeight; return totalHeight; } static boolean graphLimitsAllSame(struct track *trackList, struct track **retFirstTrack) /* Return TRUE if graphUpperLimit and graphLowerLimit same for all tracks. */ { struct track *firstTrack = NULL; struct track *track; for (track = trackList; track != NULL; track = track->next) { if (isSubtrackVisible(track) && !track->networkErrMsg) { if (firstTrack == NULL) *retFirstTrack = firstTrack = track; else if (track->graphUpperLimit != firstTrack->graphUpperLimit || track->graphLowerLimit != firstTrack->graphLowerLimit) return FALSE; } } return firstTrack != NULL; } static void multiWigLeftLabels(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 left labels - by deferring to first subtrack. */ { struct wigCartOptions *wigCart = tg->wigCartData; if (wigCart->aggregateFunction != wiggleAggregateNone) { struct track *firstVisibleSubtrack = NULL; boolean showNumbers = graphLimitsAllSame(tg->subtracks, &firstVisibleSubtrack); struct track *subtrack = (showNumbers ? firstVisibleSubtrack : tg->subtracks); wigLeftAxisLabels(tg, seqStart, seqEnd, hvg, xOff, yOff, width, height, withCenterLabels, font, color, vis, tg->shortLabel, subtrack->graphUpperLimit, subtrack->graphLowerLimit, showNumbers); } else { struct track *subtrack; int y = yOff; if (withCenterLabels) y += tl.fontHeight+1; for (subtrack = tg->subtracks; subtrack != NULL; subtrack = subtrack->next) { if (isSubtrackVisible(subtrack)) { int height = subtrack->totalHeight(subtrack, vis); if (vis == tvDense) { /* Avoid wigLeftAxisLabels here because it will repeatedly add center label * offsets, and in dense mode we will only draw the one label. */ hvGfxTextRight(hvg, xOff, y, width - 1, height, subtrack->ixColor, font, subtrack->shortLabel); } else { // black labels for readability wigLeftAxisLabels(subtrack, seqStart, seqEnd, hvg, xOff, y, width, height, withCenterLabels, font, MG_BLACK, vis, subtrack->shortLabel, subtrack->graphUpperLimit, subtrack->graphLowerLimit, TRUE); } y += height+1; } } } } void multiWigLoadItems(struct track *track) /* Load multiWig items. */ { containerLoadItems(track); struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { subtrack->mapsSelf = FALSE; /* Round about way to tell wig not to do own mapping. */ } } void multiWigContainerMethods(struct track *track) /* Override general container methods for multiWig. */ { track->syncChildVisToSelf = TRUE; track->loadItems = multiWigLoadItems; track->totalHeight = multiWigTotalHeight; track->preDrawItems = multiWigPreDraw; track->preDrawMultiRegion = multiWigMultiRegionGraphLimits; track->drawItems = multiWigDraw; track->drawLeftLabels = multiWigLeftLabels; }