8dc2f2a3bfe8d47e02a94ca4d9f62063df86b15c
braney
  Thu Oct 5 14:56:25 2017 -0700
ongoing work on hgCollection.  Add the ability to add tracks in hgTracks
to a collection.   Turn "overlay method" back on for custom collections.
Fix panning bug in hgTracks with custom collections

diff --git src/hg/hgTracks/multiWig.c src/hg/hgTracks/multiWig.c
index 8094afe..dfe7518 100644
--- src/hg/hgTracks/multiWig.c
+++ src/hg/hgTracks/multiWig.c
@@ -1,647 +1,647 @@
 /* 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; y<height; ++y)
     {
     float *fp = pic->lines[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; i<width; ++i, offset++,prevOffset++)
 	{
 	double val = p->smooth;
 
 	if (p->count)
 	    {
 	    if (yOffsets)
 		yOffsets[offset] = val;
 
             if ((numTrack == 0) || (yOffsets == NULL))
 		{
 		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;
 }
 
 
 void AllocPixelBins(struct wigGraphOutput *wgo, int width)
 {
 struct pixelCountBin *bins;
 
 AllocVar(bins);
 
 wgo->pixelBins = bins;
 bins->binSize = 1;
 bins->binCount = width / bins->binSize + 1;
 AllocArray(bins->bins, bins->binCount);
 }
 
 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 = NULL;
 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 wiggleAggregateSubtract:
     case wiggleAggregateNone:
     case wiggleAggregateAdd:
     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;
 	}
     }
 AllocPixelBins(wgo, width);
 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 mergeWiggles(struct track *tg, boolean add)
 {
 struct preDrawContainer *firstPre = tg->preDrawContainer;
 
 struct track *firstTrack = tg;
 //struct wigCartOptions *wigCart = (struct wigCartOptions *) tg->wigCartData;
 int ii;
 
 for(tg = tg->next; tg; tg = tg->next)
     {
     struct preDrawContainer *pre = tg->preDrawContainer;
 
     for(ii=firstPre->preDrawZero; ii < firstPre->preDrawZero + firstPre->width; ii++)
         {
         struct preDrawElement *firstPreDraw = &firstPre->preDraw[ii];
         struct preDrawElement *thisPreDraw = &pre->preDraw[ii];
         if (add)
             firstPreDraw->smooth += thisPreDraw->smooth;
         else
             firstPreDraw->smooth -= thisPreDraw->smooth;
         }
     }
 //if (wigCart->autoScale == wiggleScaleAuto)
     {
     double upperLimit = wigEncodeStartingUpperLimit;
     double lowerLimit = wigEncodeStartingLowerLimit;
     for(ii=firstPre->preDrawZero; ii < firstPre->preDrawZero + firstPre->width; ii++)
         {
         struct preDrawElement *firstPreDraw = &firstPre->preDraw[ii];
         if (firstPreDraw->smooth < lowerLimit)
             lowerLimit = firstPreDraw->smooth;
         if (firstPreDraw->smooth > upperLimit)
             upperLimit = firstPreDraw->smooth;
         }
     firstTrack->graphLowerLimit = lowerLimit;
     firstTrack->graphUpperLimit = upperLimit;
     firstPre->graphLowerLimit = lowerLimit;
     firstPre->graphUpperLimit = upperLimit;
     }
 }
 
 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 && 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, fullInsideX, yOff, fullInsideWidth, tg->height, yellow);
     hvGfxUnclip(hvg);
     hvGfxSetClip(hvg, clipXBak, clipYBak, clipWidthBak, clipHeightBak);
     }
 
 struct wigCartOptions *wigCart = tg->wigCartData;
 struct wigGraphOutput *wgo = tg->wigGraphOutput;
 
 if (wigCart->aggregateFunction == wiggleAggregateAdd || wigCart->aggregateFunction == wiggleAggregateSubtract)
     {
     mergeWiggles(tg->subtracks, wigCart->aggregateFunction == wiggleAggregateAdd);
     tg->subtracks->next = NULL;
     }
 int numTrack = 0;
+int height = tg->totalHeight(tg, vis); // use the parent track for the height
 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;
 }