06d7be056190c14b85e71bc12523f18ea6815b5e markd Mon Dec 7 00:50:29 2020 -0800 BLAT mmap index support merge with master diff --git src/hg/hgTracks/wigTrack.c src/hg/hgTracks/wigTrack.c index b78a3e0..a49dea9 100644 --- src/hg/hgTracks/wigTrack.c +++ src/hg/hgTracks/wigTrack.c @@ -832,132 +832,125 @@ return wgo; } struct wigGraphOutput *wigGraphOutputSolid(int xOff, int yOff, struct hvGfx *image) /* Get appropriate wigGraphOutput for non-transparent rendering */ { struct wigGraphOutput *wgo; AllocVar(wgo); wgo->image = image; wgo->vLine = vLineViaHvg; wgo->xOff = xOff; wgo->yOff = yOff; return wgo; } -/* prototype version of mouseOverData using this static array, - * will alter this to be a private linked list - */ -static struct wigMouseOver *mouseOverData = NULL; -static int mouseOverIdx = -1; - -struct wigMouseOver - { - int x; /* x,y coordinates bottom left corner of box */ - int y; - int width; /* width,height of this box */ - int height; - double value; /* data value for this region */ - }; - -void graphPreDraw(struct preDrawElement *preDraw, int preDrawZero, int width, +struct wigMouseOver *graphPreDraw(struct preDrawElement *preDraw, int preDrawZero, int width, struct track *tg, void *image, WigVerticalLineVirtual vLine, int xOff, int yOff, double *yOffsets, int numTrack, double graphUpperLimit, double graphLowerLimit, double graphRange, double epsilon, Color *colorArray, enum trackVisibility vis, struct wigCartOptions *wigCart, struct pixelCountBin *pixelBins) -/* graph the preDraw array */ +/* graph the preDraw array, returns mouse over data, or NULL */ { int x1; int h = tg->lineHeight; /* the height of our drawing window */ double scaleFactor = h/graphRange; Color oldDrawColor = colorArray[0] + 1; /* Just to be different from 1st drawColor. */ Color mediumColor = MG_BLACK; // Will be overriden Color lightColor = MG_BLACK; // Will be overriden Color clipColor = MG_MAGENTA; enum wiggleTransformFuncEnum transformFunc = wigCart->transformFunc; enum wiggleGraphOptEnum lineBar = wigCart->lineBar; boolean whiskers = (wigCart->windowingFunction == wiggleWindowingWhiskers && width < winEnd-winStart); +struct wigMouseOver *mouseOverData = NULL; + /* list of mouse over data, if created here */ -AllocArray(mouseOverData, width); - +boolean skipMouseOvers = TRUE; /* assuming not using */ int mouseOverX2 = -1; double previousValue = 0; -boolean skipMouseOvers = FALSE; -#define epsilonLimit 1.0e-6 -// if (psOutput) -// skipMouseOvers = TRUE; +if (enableMouseOver) + skipMouseOvers = FALSE; /* right now this is a simple pixel by pixel loop. Future * enhancements could draw boxes where pixels * are all the same height in a run. */ for (x1 = 0; x1 < width; ++x1) { int x = x1 + xOff; int preDrawIndex = x1 + preDrawZero; struct preDrawElement *p = &preDraw[preDrawIndex]; /* ===== mouseOver calculations===== */ + if (enableMouseOver) + { if (!skipMouseOvers && (p->count > 0)) /* checking mouseOver construction */ { - if (p->count < 3) /* allow 1 or 2 values to display */ + if (p->count > 0) /* allow any number of values to display */ { - double thisValue = p->sumData/p->count; /* average if 2 */ + double thisValue = p->sumData/p->count; /*average if count > 1*/ if (mouseOverX2 < 0) /* first valid data found */ { - ++mouseOverIdx; + struct wigMouseOver *dataItem; + AllocVar(dataItem); mouseOverX2 = x1+1; - mouseOverData[mouseOverIdx].x = xOff+x1; - mouseOverData[mouseOverIdx].width = mouseOverX2 - x1; - mouseOverData[mouseOverIdx].y = yOff; - mouseOverData[mouseOverIdx].height = h; - mouseOverData[mouseOverIdx].value = thisValue; + dataItem->x1 = x1; + dataItem->x2 = mouseOverX2; + dataItem->value = thisValue; + dataItem->valueCount = p->count; + slAddHead(&mouseOverData, dataItem); previousValue = thisValue; } else /* see if we need a new item */ { +#define epsilonLimit 1.0e-6 if (fabs(thisValue - previousValue) > epsilonLimit) { - /* finish off the existing run of data */ - mouseOverData[mouseOverIdx].width = mouseOverX2 - (mouseOverData[mouseOverIdx].x - xOff); + /* finish off the existing run of data (list head)*/ + mouseOverData->x2 = mouseOverX2; mouseOverX2 = x1+1; - ++mouseOverIdx; - mouseOverData[mouseOverIdx].x = xOff+x1; - mouseOverData[mouseOverIdx].width = mouseOverX2 - x1; - mouseOverData[mouseOverIdx].y = yOff; - mouseOverData[mouseOverIdx].height = h; - mouseOverData[mouseOverIdx].value = thisValue; + struct wigMouseOver *dataItem; + AllocVar(dataItem); + dataItem->x1 = x1; + dataItem->x2 = mouseOverX2; + dataItem->value = thisValue; + dataItem->valueCount = p->count; + slAddHead(&mouseOverData, dataItem); previousValue = thisValue; } else /* continue run of same data value */ mouseOverX2 = x1+1; } } else skipMouseOvers = TRUE; /* has become too dense to make sense */ } else /* perhaps entered region without values after some data already */ { if (mouseOverX2 > 0) /* yes, been in data, end it here */ { - mouseOverData[mouseOverIdx].width = mouseOverX2 - (mouseOverData[mouseOverIdx].x - xOff); + mouseOverData->x2 = mouseOverX2; mouseOverX2 = -1; /* start over with new data when found*/ } } /* potentially end the last mouseOver box */ - if (mouseOverX2 > 0 && (mouseOverX2 - (mouseOverData[mouseOverIdx].x - xOff)) > mouseOverData[mouseOverIdx].width) - mouseOverData[mouseOverIdx].width = mouseOverX2 - (mouseOverData[mouseOverIdx].x - xOff); + if (mouseOverX2 > 0 && mouseOverX2 > mouseOverData->x2) + mouseOverData->x2 = mouseOverX2; + + } // if (enableMouseOver) + else + skipMouseOvers = TRUE; /* ===== done with mouseOver calculations===== */ assert(x1/pixelBins->binSize < pixelBins->binCount); unsigned long *bitCount = &pixelBins->bins[x1/pixelBins->binSize]; Color drawColor = colorArray[x1]; if (drawColor != oldDrawColor) { mediumColor = somewhatLighterColor32(drawColor); lightColor = somewhatLighterColor32(mediumColor); oldDrawColor = drawColor; } /* count is non-zero meaning valid data exists here */ @@ -1147,52 +1140,50 @@ else if (vis == tvDense || vis == tvSquish) { double grayValue; int grayIndex; /* honor the viewLimits, data below is white, data above is black */ grayValue = max(dataValue,graphLowerLimit); grayValue = min(grayValue,graphUpperLimit); grayIndex = ((grayValue-graphLowerLimit)/graphRange)*MAX_WIG_VALUE; drawColor = tg->colorShades[grayInRange(grayIndex, 0, MAX_WIG_VALUE)]; doLine(image, x, yOff, tg->lineHeight, drawColor); } /* vis == tvDense || vis == tvSquish */ } /* if (preDraw[].count) */ } /* for (x1 = 0; x1 < width; ++x1) */ - - if (skipMouseOvers || mouseOverIdx < 0) - freez(&mouseOverData); - +return(mouseOverData); } /* graphPreDraw() */ -static void graphPreDrawContainer(struct preDrawContainer *preDrawContainer, +static struct wigMouseOver *graphPreDrawContainer(struct preDrawContainer *preDrawContainer, int preDrawZero, int width, struct track *tg, struct hvGfx *hvg, int xOff, int yOff, double graphUpperLimit, double graphLowerLimit, double graphRange, enum trackVisibility vis, struct wigCartOptions *wigCart) /* Draw the graphs for all tracks in container. */ { double epsilon = graphRange / tg->lineHeight; struct preDrawElement *preDraw = preDrawContainer->preDraw; Color *colorArray = makeColorArray(preDraw, width, preDrawZero, wigCart, tg, hvg); struct wigGraphOutput *wgo = tg->wigGraphOutput; -graphPreDraw(preDraw, preDrawZero, width, - tg, wgo->image, wgo->vLine, wgo->xOff, wgo->yOff, wgo->yOffsets, wgo->numTrack, - graphUpperLimit, graphLowerLimit, graphRange, +struct wigMouseOver *mouseOverData = graphPreDraw(preDraw, preDrawZero, width, + tg, wgo->image, wgo->vLine, wgo->xOff, wgo->yOff, wgo->yOffsets, + wgo->numTrack, graphUpperLimit, graphLowerLimit, graphRange, epsilon, colorArray, vis, wigCart, wgo->pixelBins); freez(&colorArray); +return mouseOverData; } void drawZeroLine(enum trackVisibility vis, enum wiggleGridOptEnum horizontalGrid, double graphUpperLimit, double graphLowerLimit, struct hvGfx *hvg, int xOff, int yOff, int width, int lineHeight) /* draw a line at y=0 on the graph */ { /* Do we need to draw a zero line ? * This is to be generalized in the future to allow horizontal grid * lines, perhaps user specified to indicate thresholds. */ if ((vis == tvFull) && (horizontalGrid == wiggleHorizontalGridOn)) { @@ -1420,85 +1411,73 @@ tg->graphUpperLimit = graphUpperLimit = tg->tdb->parent->tdbExtras->minMax->max; } /* if we're autoscaling and the range is 0 this implies that all values * in the given range are the same. We create a bottom of the scale * by subtracting one from the only value. * This results in drawing a box that fills the range. */ if (graphUpperLimit == graphLowerLimit) { graphLowerLimit = graphUpperLimit - 1; } graphRange = graphUpperLimit - graphLowerLimit; wigTrackSetGraphOutputDefault(tg, xOff, yOff, width, hvg); -graphPreDrawContainer(preContainer, preDrawZero, width, tg, hvg, xOff, yOff, +struct wigMouseOver *mouseOverData = graphPreDrawContainer(preContainer, + preDrawZero, width, tg, hvg, xOff, yOff, graphUpperLimit, graphLowerLimit, graphRange, vis, wigCart); drawZeroLine(vis, wigCart->horizontalGrid, graphUpperLimit, graphLowerLimit, hvg, xOff, yOff, width, tg->lineHeight); drawArbitraryYLine(vis, (enum wiggleGridOptEnum)wigCart->yLineOnOff, graphUpperLimit, graphLowerLimit, hvg, xOff, yOff, width, tg->lineHeight, wigCart->yLineMark, graphRange, wigCart->yLineOnOff); -#ifdef NOT_READY_TO_GO -if (mouseOverData) - { - static boolean beenHereDoneThat = FALSE; - struct tempName jsonData; - trashDirFile(&jsonData, "hgt", tg->track, ".json"); - FILE *trashJson = mustOpen(jsonData.forCgi, "w"); - struct jsonWrite *jw = jsonWriteNew(); - jsonWriteObjectStart(jw, NULL); - jsonWriteListStart(jw, tg->track); - int i; - /* could put up a 'no data' box when these items are not contiguous - * e.g. when gaps interrupt the track data - */ - for (i = 0; i <= mouseOverIdx; ++i) - { - jsonWriteObjectStart(jw, NULL); - jsonWriteNumber(jw, "x1", (long long)mouseOverData[i].x); - jsonWriteNumber(jw, "y1", (long long)mouseOverData[i].y); - jsonWriteNumber(jw, "x2", (long long)(mouseOverData[i].x + mouseOverData[i].width)); - jsonWriteNumber(jw, "y2", (long long)(mouseOverData[i].y + mouseOverData[i].height)); - jsonWriteDouble(jw, "v", mouseOverData[i].value); - jsonWriteObjectEnd(jw); - } - jsonWriteListEnd(jw); - jsonWriteObjectEnd(jw); - fputs(jw->dy->string,trashJson); - // This is the hidden signal to the javaScript of where to pick up - // the json file - hPrintf("<MAP Name=%s class=mouseOver trashFile='%s'>\n", tg->track, jsonData.forHtml); - hPrintf("</MAP>\n"); - carefulClose(&trashJson); - if (! beenHereDoneThat ) - { - hPrintf("<div id='mouseOverContainer' class='wigMouseOver'>\n"); - hPrintf(" <span id='mouseOverText' class=wigMouseOverValue'>\n"); - hPrintf(" </span>\n"); - hPrintf("</div>\n"); - beenHereDoneThat = TRUE; - } - } -else -#endif +if (enableMouseOver && mouseOverData) + { + jsonWriteObjectStart(mouseOverJson, tg->track); + jsonWriteString(mouseOverJson, "t", tg->tdb->type); + jsonWriteListStart(mouseOverJson, "d"); + slReverse(&mouseOverData); + struct wigMouseOver *dataItem = mouseOverData; + for (; dataItem; dataItem = dataItem->next) + { + jsonWriteObjectStart(mouseOverJson, NULL); + jsonWriteNumber(mouseOverJson, "x1", (long long)dataItem->x1); + jsonWriteNumber(mouseOverJson, "x2", (long long)dataItem->x2); + jsonWriteDouble(mouseOverJson, "v", dataItem->value); + jsonWriteNumber(mouseOverJson, "c", dataItem->valueCount); + jsonWriteObjectEnd(mouseOverJson); + } + jsonWriteListEnd(mouseOverJson); + jsonWriteObjectEnd(mouseOverJson); + slFreeList(&mouseOverData); + // hidden element to pass along jsonUrl file name and also the trigger + // that this track has data to display. + hPrintf("<div id='mouseOver_%s' name='%s' class='hiddenText mouseOverData' jsonUrl='%s'></div>\n", tg->track, tg->track, mouseOverJsonFile->forCgi); + } +// Might need something like this later for other purposes +// else if (enableMouseOver) // system enabled, but no data for this track +// { + /* signal to indicate zoom in required to see data */ +// hPrintf("<div id='mouseOver_%s' name='%s' class='hiddenText mouseOverData'></div>\n", tg->track, tg->track); +// } + wigMapSelf(tg, hvg, seqStart, seqEnd, xOff, yOff, width); } struct preDrawContainer *wigLoadPreDraw(struct track *tg, int seqStart, int seqEnd, int width) /* Do bits that load the predraw buffer tg->preDrawContainer. */ { /* Just need to do this once... */ if (tg->preDrawContainer) return tg->preDrawContainer; struct wigItem *wi; double pixelsPerBase = scaleForPixels(width); double basesPerPixel = 1.0; int itemCount = 0; char *currentFile = NULL; @@ -1571,57 +1550,65 @@ * * The data to be drawn: to be read from file f at offset wi->Offset * data points available: wi->Count, representing wi->Span bases * for each data point * * The drawing window, in pixels: * xOff = left margin, yOff = top margin, h = height of drawing window * drawing window in chrom coords: seqStart, seqEnd * 'basesPerPixel' is known, 'pixelsPerBase' is known */ /* let's check end point screen coordinates. If they are * the same, then this entire data block lands on one pixel, * no need to walk through it, just use the block's specified * max/min. It is OK if these end up + or - values, we do want to * keep track of pixels before and after the screen for - * later smoothing operations. + * later smoothing operations. x1d,x2d are pixel coordinates */ double x1d = (double)(wi->start - seqStart) * pixelsPerBase; x1 = round(x1d); double x2d = (double)((wi->start+(wi->count * usingDataSpan))-seqStart) * pixelsPerBase; x2 = round(x2d); /* this used to be if (x2 > x1) which often caused reading of blocks * when they were merely x2 = x1 + 1 due to rounding errors as * they became integers. This double comparison for something over * 0.5 will account for rounding errors that are really small, but * still handle a slipping window size as it walks across the screen */ if ((x2d - x1d) > 0.5) { unsigned char *readData; /* the bytes read in from the file */ udcSeek(wibFH, wi->offset); readData = (unsigned char *) needMem((size_t) (wi->count + 1)); udcRead(wibFH, readData, (size_t) wi->count * (size_t) sizeof(unsigned char)); - /* walk through all the data in this block */ + /* walk through all the data in this wiggle data block */ for (dataOffset = 0; dataOffset < wi->count; ++dataOffset) { unsigned char datum = readData[dataOffset]; if (datum != WIG_NO_DATA) { + /* (wi->start-seqStart) == base where this wiggle data block + * begins. Add to that (dataOffset * usingDataSpan) which + * is how many bases this specific datum is from the start + * of this wiggle data block. + * x1,x2 are the pixel begin and end for this data item */ x1 = ((wi->start-seqStart) + (dataOffset * usingDataSpan)) * pixelsPerBase; + /* (usingDataSpan * pixelsPerBase) is the number of pixels + * occupied by this one data item + */ x2 = x1 + (usingDataSpan * pixelsPerBase); for (i = x1; i <= x2; ++i) { int xCoord = preDrawZero + i; if ((xCoord >= 0) && (xCoord < preDrawSize)) { double dataValue = BIN_TO_VALUE(datum,wi->lowerLimit,wi->dataRange); ++preDraw[xCoord].count; if (dataValue > preDraw[xCoord].max) preDraw[xCoord].max = dataValue; if (dataValue < preDraw[xCoord].min) preDraw[xCoord].min = dataValue; preDraw[xCoord].sumData += dataValue;