06d7be056190c14b85e71bc12523f18ea6815b5e
markd
  Mon Dec 7 00:50:29 2020 -0800
BLAT mmap index support merge with master

diff --git src/hg/hgTracks/lollyTrack.c src/hg/hgTracks/lollyTrack.c
index 232f2f1..f0471da 100644
--- src/hg/hgTracks/lollyTrack.c
+++ src/hg/hgTracks/lollyTrack.c
@@ -1,125 +1,140 @@
 /* lollyTrack -- load and draw lollys */
 
 /* Copyright (C) 2019 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "obscure.h"
 #include "hgTracks.h"
 #include "bedCart.h"
 #include "bigWarn.h"
 #include "lolly.h"
 #include "limits.h"
 #include "float.h"
 #include "bigBedFilter.h"
+#include "asParse.h"
 
 #define LOLLY_DIAMETER    2 * (lollyCart->radius + 2)
 
 struct lolly
 {
 struct lolly *next;
 char *name;       /* the mouseover name */
 double val;       /* value in the data file */   
 unsigned start;   /* genomic start address */
 unsigned end;     /* genomic end address */
 unsigned radius;  /* radius of the top of the lolly */
 unsigned height;  /* height of the lolly */
 Color color;      /* color of the lolly */
 char *mouseOver;
 };
 
 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);
-    int number = atoi(nextWord(&setting)); 
+    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);
@@ -142,99 +157,112 @@
 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);
-    hvGfxTextRight(hvg, xOff, yOff+height - LOLLY_DIAMETER - fontHeight/2 , width - 1, fontHeight,
+    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;
 }
 
 int cmpHeight(const void *va, const void *vb)
 // sort the lollies by height 
 {
 const struct lolly *a = *((struct lolly **)va);
 const struct lolly *b = *((struct lolly **)vb);
 return a->height - b->height;
 }
 
+static int getField(struct trackDb *tdb, char *name, struct asObject *as, int defaultValue)
+/* Get the as field (if any) that a trackDb variable references.  This can either be
+ * a field name, or a number. */
+{
+int ret = defaultValue;
+char *setting = trackDbSetting(tdb, name);
+if (setting != NULL)
+    {
+    if (isNumericString(setting))
+        ret = atoi(setting);
+    else 
+        {
+        struct asColumn *columns = as->columnList;
+        ret = asColumnMustFindIx(columns, setting) + 1;
+        }
+    }
+return ret;
+}
+
 void lollyLoadItems(struct track *tg)
 // load lollies from the data file
 {
 struct lollyCartOptions *lollyCart = tg->lollyCart;
 if (tg->visibility == tvSquish)
     {
     //lollyCart->radius = 2;
     lollyCart->height =  lollyCart->origHeight / 2;
     }
 else if (tg->visibility == tvPack)
     {
     //lollyCart->radius = 4;
     lollyCart->height =  lollyCart->origHeight / 1.5;
     }
 struct lm *lm = lmInit(0);
 struct bbiFile *bbi =  fetchBbiForTrack(tg);
+struct asObject *as = bigBedAsOrDefault(bbi);
 struct bigBedInterval *bb, *bbList =  bigBedIntervalQuery(bbi, chromName, winStart, winEnd, 0, lm);
 char *bedRow[bbi->fieldCount];
 char startBuf[16], endBuf[16];
 struct lolly *popList = NULL, *pop;
 
-unsigned lollyField = 5;  // we use the score field by default
-char *setting = trackDbSetting(tg->tdb, "lollyField");
-if (setting != NULL)
-    lollyField = atoi(setting);
-
-int lollySizeField = -1 ;
-setting = trackDbSetting(tg->tdb, "lollySizeField");
-if (setting != NULL)
-    lollySizeField = atoi(setting);
+unsigned lollyField = getField(tg->tdb, "lollyField", as, 5);  // we use the score field by default
+int lollySizeField = getField(tg->tdb, "lollySizeField", as, -1);  // no size field by default
 
 double minVal = DBL_MAX, maxVal = -DBL_MAX;
 //double sumData = 0.0, sumSquares = 0.0;
 unsigned count = 0;
 
 int trackHeight = tg->lollyCart->height;
 struct bigBedFilter *filters = bigBedBuildFilters(cart, bbi, tg->tdb);
 char *mouseOverField = cartOrTdbString(cart, tg->tdb, "mouseOverField", NULL);
 int mouseOverIdx = bbExtraFieldIndex(bbi, mouseOverField) ;
 
 char *mouseOverPattern = NULL;
 char **fieldNames = NULL;
 if (!mouseOverIdx)
     {
     mouseOverPattern = cartOrTdbString(cart, tg->tdb, "mouseOver", NULL);
@@ -289,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;
 }