9dd986a7efee751199f4caa5d4b66dbabb479854
braney
  Mon Apr 29 08:23:01 2019 -0700
clean up lollyTrack.c for "final" code review

diff --git src/hg/hgTracks/lollyTrack.c src/hg/hgTracks/lollyTrack.c
new file mode 100644
index 0000000..d76e324
--- /dev/null
+++ src/hg/hgTracks/lollyTrack.c
@@ -0,0 +1,308 @@
+/* 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"
+
+#define LOLLY_RADIUS    5
+#define LOLLY_DIAMETER    2 * LOLLY_RADIUS
+
+
+/* the lolly colors */
+static int lollyPalette[] =
+{
+0x1f77b4, 0xff7f0e, 0x2ca02c, 0xd62728, 0x9467bd, 0x8c564b, 0xe377c2, 0x7f7f7f, 0xbcbd22, 0x17becf
+};
+
+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 */
+};
+
+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;
+int trackHeight = tg->lollyCart->height;
+
+if (popList == NULL)   // nothing to draw
+    return;
+
+if ( tg->visibility == tvDense)  // in dense mode we just track lines
+    {
+    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, sx , yOff+ tl.fontHeight, pop->color);
+        }
+    return;
+    }
+
+// first draw the lines so they won't overlap any lollies
+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+(trackHeight - 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)
+    hvGfxCircle(hvg, sx, yOff + trackHeight - pop->radius - pop->height, pop->radius, pop->color, TRUE);
+    mapBoxHgcOrHgGene(hvg, pop->start, pop->end, sx - pop->radius, yOff + trackHeight - pop->radius - pop->height - pop->radius, 2 * pop->radius,2 * pop->radius,
+                      tg->track, pop->name, pop->name, NULL, TRUE, NULL);
+    }
+}
+
+void lollyLeftLabels(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 the labels on the left margin
+{
+int fontHeight = tl.fontHeight+1;
+int centerLabel = (height/2)-(fontHeight/2);
+if ( tg->visibility == tvDense)
+    {
+    hvGfxText(hvg, xOff, yOff+fontHeight, color, font, tg->shortLabel);
+    return;
+    }
+
+hvGfxText(hvg, xOff, yOff+centerLabel, color, font, tg->shortLabel);
+
+if (isnan(tg->lollyCart->upperLimit))
+    {
+    hvGfxTextRight(hvg, xOff, yOff + LOLLY_DIAMETER, width - 1, fontHeight, color,
+        font, "NO DATA");
+    return;
+    }
+
+char upper[1024];
+safef(upper, sizeof(upper), "%g -",   tg->lollyCart->upperLimit);
+hvGfxTextRight(hvg, xOff, yOff + LOLLY_DIAMETER, width - 1, fontHeight, color,
+    font, upper);
+char lower[1024];
+if (tg->lollyCart->lowerLimit < tg->lollyCart->upperLimit)
+    {
+    safef(lower, sizeof(lower), "%g _", tg->lollyCart->lowerLimit);
+    hvGfxTextRight(hvg, xOff, yOff+height-fontHeight - LOLLY_DIAMETER, 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;
+}
+
+#ifdef NOTUSED
+double calcVarianceFromSums(double sum, double sumSquares, bits64 n)
+/* Calculate variance. */
+{   
+double var = sumSquares - sum*sum/n;
+if (n > 1)
+    var /= n-1;
+return var;
+}   
+    
+double calcStdFromSums(double sum, double sumSquares, bits64 n)
+/* Calculate standard deviation. */
+{   
+return sqrt(calcVarianceFromSums(sum, sumSquares, n));
+}
+#endif // NOTUSED
+
+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;
+}
+
+void lollyLoadItems(struct track *tg)
+// load lollies from the data file
+{
+struct lm *lm = lmInit(0);
+struct bbiFile *bbi =  fetchBbiForTrack(tg);
+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
+struct lollyCartOptions *lollyCart = tg->lollyCart;
+char *setting = trackDbSetting(tg->tdb, "lollyField");
+if (setting != NULL)
+    lollyField = atoi(setting);
+
+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);
+                    
+for (bb = bbList; bb != NULL; bb = bb->next)
+    {
+    bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow));
+
+    // throw away items that don't pass the filters
+    if (!bigBedFilterInterval(bedRow, filters))
+        continue;
+
+    // clip out lollies that aren't in our display range
+    double val = atof(bedRow[lollyField - 1]);
+    if (!((lollyCart->autoScale == wiggleScaleAuto) ||  ((val >= lollyCart->minY) && (val <= lollyCart->maxY) )))
+        continue;
+
+    // don't draw lollies off the screen
+    if (atoi(bedRow[1]) < winStart)
+        continue;
+
+    AllocVar(pop);
+    slAddHead(&popList, pop);
+    pop->val = val;
+    pop->start = atoi(bedRow[1]);
+    pop->end = atoi(bedRow[2]);
+    pop->name = cloneString(bedRow[3]);
+    count++;
+    // sumData += val;
+    // sumSquares += val * val;
+    if (val > maxVal)
+        maxVal = val;
+    if (val < minVal)
+        minVal = val;
+    }
+
+if (count == 0)
+    lollyCart->upperLimit = lollyCart->lowerLimit = NAN; // no lollies in range
+else if (lollyCart->autoScale == wiggleScaleAuto)
+    {
+    lollyCart->upperLimit = maxVal;
+    lollyCart->lowerLimit = minVal;
+    }
+
+double range = lollyCart->upperLimit - lollyCart->lowerLimit;
+int usableHeight = trackHeight - 2 * LOLLY_DIAMETER; 
+for(pop = popList; pop; pop = pop->next)
+    {
+    pop->radius = LOLLY_RADIUS;
+    pop->color = MG_RED;
+    if (range == 0.0)
+        {
+        pop->height = usableHeight ;
+        pop->color = lollyPalette[0] | 0xff000000;
+        }
+    else
+        {
+        pop->height = usableHeight * (pop->val  - lollyCart->lowerLimit) / range + LOLLY_DIAMETER;
+        int colorIndex = 8 * (pop->val  - lollyCart->lowerLimit) / range;
+        pop->color = lollyPalette[colorIndex] | 0xff000000;
+        }
+    }
+
+#ifdef NOTUSED   // a method of scaling assuming a sort of normal distribution
+double average = sumData/count;
+double stdDev = calcStdFromSums(sumData, sumSquares, count);
+
+for(pop = popList; pop; pop = pop->next)
+    {
+    if (pop->val > average + stdDev / 5)
+        {
+        pop->color = MG_RED;
+        pop->radius = 8;
+        pop->height = 3 * tl.fontHeight;
+        }
+    else if (pop->val < average - stdDev / 5)
+        {
+        pop->color = MG_GREEN;
+        pop->radius = 3;
+        pop->height = 1 * tl.fontHeight;
+        }
+    else
+        {
+        pop->color = MG_GRAY;
+        pop->radius = 5;
+        pop->height = 2 * tl.fontHeight;
+        }
+        
+    }
+#endif // NOTUSED
+
+slSort(&popList, cmpHeight);
+tg->items = popList;
+}
+
+static struct lollyCartOptions *lollyCartOptionsNew(struct cart *cart, struct trackDb *tdb, 
+                                int wordCount, char *words[])
+// the structure that is attached to the track structure
+{
+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->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;
+
+return lollyCart;
+}
+
+void lollyMethods(struct track *track, struct trackDb *tdb, 
+                                int wordCount, char *words[])
+/* bigLolly track type methods */
+{
+struct lollyCartOptions *lollyCart = lollyCartOptionsNew(cart, tdb, wordCount, words);
+char *ourWords[2];
+ourWords[0] = "bigBed";
+ourWords[1] = "4";
+bigBedMethods(track, tdb,2,ourWords);
+if (tdb->visibility == tvDense)
+    return;
+track->loadItems = lollyLoadItems;
+track->drawItems = lollyDrawItems;
+track->totalHeight = lollyHeight; 
+track->drawLeftLabels = lollyLeftLabels;
+track->lollyCart = lollyCart;
+}