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;