6985ec2b43b98f50d26ff7b3f72cefce3f6d5eba max Tue Sep 11 06:29:55 2018 -0700 updating phegenToBed, refs #21052 diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c index 83c6ca7..82df923 100644 --- src/hg/hgTracks/barChartTrack.c +++ src/hg/hgTracks/barChartTrack.c @@ -10,45 +10,48 @@ #include "spaceSaver.h" #include "hubConnect.h" #include "barChartBed.h" #include "barChartCategory.h" #include "barChartUi.h" // If a category contributes more than this percentage, its color is displayed in squish mode // Could be a trackDb setting #define SPECIFICITY_THRESHOLD 10 struct barChartTrack /* Track info */ { boolean noWhiteout; /* Suppress whiteout of graph background (allow highlight, blue lines) */ double maxMedian; /* Maximum median across all categories */ + int maxHeight; /* Maximum height in pixels for track */ boolean doLogTransform; /* Log10(x+1) */ + boolean doAutoScale; /* Scale to maximum in window, alternative to log */ char *unit; /* Units for category values (e.g. RPKM) */ struct barChartCategory *categories; /* Category names, colors, etc. */ int categCount; /* Count of categories - derived from above */ char **categNames; /* Category names - derived from above */ char **categLabels; /* Category labels - derived from above */ struct rgbColor *colors; /* Colors for all categories */ struct hash *categoryFilter; /* NULL out excluded factors */ }; struct barChartItem /* BED item plus computed values for display */ { struct barChartItem *next; /* Next in singly linked list */ struct bed *bed; /* Item coords, name, exp count and values */ + int maxScore; /* Maximum expScore in bed */ int height; /* Item height in pixels */ }; /***********************************************/ /* Organize category info */ struct barChartCategory *getCategories(struct track *tg) /* Get and cache category info */ { struct barChartTrack *info = (struct barChartTrack *)tg->extraUiData; if (info->categories == NULL) info->categories = barChartUiGetCategories(database, tg->tdb); return info->categories; } @@ -137,30 +140,45 @@ #define WIN_MAX_GRAPH 50000 #define WIN_MED_GRAPH 500000 #define MAX_BAR_CHART_MODEL_HEIGHT 2 #define MED_BAR_CHART_MODEL_HEIGHT 2 #define MIN_BAR_CHART_MODEL_HEIGHT 1 if (winSize < WIN_MAX_GRAPH) return MAX_BAR_CHART_MODEL_HEIGHT; else if (winSize < WIN_MED_GRAPH) return MED_BAR_CHART_MODEL_HEIGHT; else return MIN_BAR_CHART_MODEL_HEIGHT; } +static int barChartMaxHeight(int maxHeight) +/* Set maximum graph height based on window size */ +{ +// scale based on subjective aesthetic (previous hardcoded were 175/100) +#define WIN_MED_GRAPH_SCALE .57 + +long winSize = virtWinBaseCount; +if (winSize < WIN_MAX_GRAPH) + return maxHeight; +else if (winSize < WIN_MED_GRAPH) + return maxHeight * WIN_MED_GRAPH_SCALE; +else + return tl.fontHeight * 4; +} + static int barChartItemHeight(struct track *tg, void *item); static void filterCategories(struct track *tg) /* Check cart for category selection. NULL out unselected categorys in category list */ { struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct barChartCategory *categ = NULL; extras->categories = getCategories(tg); extras->categoryFilter = hashNew(0); if (cartListVarExistsAnyLevel(cart, tg->tdb, FALSE, BAR_CHART_CATEGORY_SELECT)) { struct slName *selectedValues = cartOptionalSlNameListClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_CATEGORY_SELECT); if (selectedValues != NULL) { @@ -204,99 +222,143 @@ expScore = bed->expScores[i]; if (expScore > maxScore) { maxScore = max(maxScore, expScore); maxNum = i; } totalScore += expScore; } // threshold to consider this item category specific -- a category contributes > threshold % to // total level if (totalScore < 1 || maxScore <= (totalScore * threshold * .01)) return -1; return maxNum; } +double barChartMaxExpScore(struct track *tg, struct barChartItem *itemInfo) +/* Determine maximum expScore in a barChart, set it and return it */ +{ +// use preset if available +double maxScore = itemInfo->maxScore; +if (maxScore > 0.0) + return maxScore; + +// determine from bed +maxScore = 0.0; +struct bed *bed = (struct bed *)itemInfo->bed; +int i; +int expCount = bed->expCount; +double expScore; +for (i=0; i<expCount; i++) + { + if (!filterCategory(tg, getCategoryName(tg, i))) + continue; + expScore = bed->expScores[i]; + maxScore = max(maxScore, expScore); + } +itemInfo->maxScore = maxScore; +return maxScore; +} + static Color barChartItemColor(struct track *tg, void *item, struct hvGfx *hvg) /* A bit of category-specific coloring in squish mode only, on bed item */ { struct bed *bed = (struct bed *)item; int id = maxCategoryForItem(bed, SPECIFICITY_THRESHOLD); if (id < 0) return MG_BLACK; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct rgbColor color = extras->colors[id]; return hvGfxFindColorIx(hvg, color.r, color.g, color.b); } static void barChartLoadItems(struct track *tg) /* Load method for track items */ { /* Initialize colors for visibilities that don't display actual barchart */ if (tg->visibility == tvSquish || tg->limitedVis == tvSquish) tg->itemColor = barChartItemColor; tg->colorShades = shadesOfGray; /* Get track UI info */ struct barChartTrack *extras; AllocVar(extras); tg->extraUiData = extras; struct trackDb *tdb = tg->tdb; extras->doLogTransform = cartUsualBooleanClosestToHome(cart, tdb, FALSE, BAR_CHART_LOG_TRANSFORM, BAR_CHART_LOG_TRANSFORM_DEFAULT); -extras->maxMedian = barChartUiMaxMedianScore(tdb); +extras->doAutoScale = cartUsualBooleanClosestToHome(cart, tdb, FALSE, BAR_CHART_AUTOSCALE, + BAR_CHART_AUTOSCALE_DEFAULT); extras->noWhiteout = cartUsualBooleanClosestToHome(cart, tdb, FALSE, BAR_CHART_NO_WHITEOUT, BAR_CHART_NO_WHITEOUT_DEFAULT); extras->unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, ""); +int min, max, deflt, current; +barChartUiFetchMinMaxPixels(cart, tdb, &min, &max, &deflt, ¤t); +extras->maxHeight = barChartMaxHeight(current); + /* Get bed (names and all-sample category median scores) in range */ loadSimpleBedWithLoader(tg, (bedItemLoader)barChartSimpleBedLoad); /* Create itemInfo items with BED and geneModels */ -struct barChartItem *itemInfo = NULL, *list = NULL; +struct barChartItem *itemInfo = NULL, *infoList = NULL; struct bed *bed = (struct bed *)tg->items; /* Load category colors */ extras->colors = getCategoryColors(tg); filterCategories(tg); +/* create list of barChart items */ while (bed != NULL) { AllocVar(itemInfo); itemInfo->bed = bed; - slAddHead(&list, itemInfo); + slAddHead(&infoList, itemInfo); bed = bed->next; itemInfo->bed->next = NULL; + itemInfo->maxScore = barChartMaxExpScore(tg, itemInfo); + // for autoscale, determine maximum median score in window + if (itemInfo->maxScore > extras->maxMedian) + extras->maxMedian = itemInfo->maxScore; + } +/* determine graph heights */ +for (itemInfo = infoList; itemInfo != NULL; itemInfo = itemInfo->next) + { itemInfo->height = barChartItemHeight(tg, itemInfo); } -slReverse(&list); -tg->items = list; +double maxScore = barChartUiMaxMedianScore(tdb); +if (!extras->doAutoScale && maxScore > extras->maxMedian) + // maximum median score in entire dataset. + // If not set, using default, but might be too small for this dataset. + extras->maxMedian = maxScore; + +/* replace item list with wrapped beds */ +slReverse(&infoList); +tg->items = infoList; } /***********************************************/ /* Draw */ /* Bargraph layouts for three window sizes */ #define WIN_MAX_GRAPH 50000 -#define MAX_GRAPH_HEIGHT 175 #define MAX_BAR_WIDTH 5 #define MAX_GRAPH_PADDING 2 #define WIN_MED_GRAPH 500000 -#define MED_GRAPH_HEIGHT 100 #define MED_BAR_WIDTH 3 #define MED_GRAPH_PADDING 1 #define MIN_BAR_WIDTH 1 #define MIN_GRAPH_PADDING 0 #define MARGIN_WIDTH 1 static int barChartBarWidth(struct track *tg) { long winSize = virtWinBaseCount; int scale = (getCategoryCount(tg) < 15 ? 2 : 1); if (winSize < WIN_MAX_GRAPH) return MAX_BAR_WIDTH * scale; @@ -311,39 +373,33 @@ return barChartBoxModelHeight()+3; } static int barChartPadding() { long winSize = virtWinBaseCount; if (winSize < WIN_MAX_GRAPH) return MAX_GRAPH_PADDING; else if (winSize < WIN_MED_GRAPH) return MED_GRAPH_PADDING; else return MIN_GRAPH_PADDING; } -static int barChartMaxHeight() +static boolean barChartUseViewLimit(struct barChartTrack *extras) { -long winSize = virtWinBaseCount; -if (winSize < WIN_MAX_GRAPH) - return MAX_GRAPH_HEIGHT; -else if (winSize < WIN_MED_GRAPH) - return MED_GRAPH_HEIGHT; -else - return tl.fontHeight * 4; +return !extras->doLogTransform && !extras->doAutoScale; } static int barChartWidth(struct track *tg, struct barChartItem *itemInfo) /* Width of bar chart in pixels */ { int barWidth = barChartBarWidth(tg); int padding = barChartPadding(); int count = filteredCategoryCount(tg); return (barWidth * count) + (padding * (count-1)) + 2; } static int barChartX(struct bed *bed) /* Locate chart on X, relative to viewport. */ { int start = max(bed->chromStart, winStart); @@ -360,65 +416,53 @@ static int valToHeight(double val, double maxVal, int maxHeight, boolean doLogTransform) /* Log-scale and convert a value from 0 to maxVal to 0 to maxHeight-1 */ { if (val == 0.0) return 0; double scaled = 0.0; if (doLogTransform) scaled = log10(val+1.0) / log10(maxVal+1.0); else scaled = val/maxVal; if (scaled < 0) warn("scaled=%f\n", scaled); return (scaled * (maxHeight-1)); } -static int valToClippedHeight(double val, double maxVal, int maxView, int maxHeight, - boolean doLogTransform) +static int valToClippedHeight(double val, double maxVal, int maxView, struct barChartTrack *extras) /* Convert a value from 0 to maxVal to 0 to maxHeight-1, with clipping, or log transform the value */ { double useVal = val; double useMax = maxVal; -if (!doLogTransform) +if (barChartUseViewLimit(extras)) { useMax = maxView; if (val > maxView) useVal = maxView; } -return valToHeight(useVal, useMax, barChartMaxHeight(), doLogTransform); +return valToHeight(useVal, useMax, extras->maxHeight, extras->doLogTransform); } static int barChartHeight(struct track *tg, struct barChartItem *itemInfo) /* Determine height in pixels of graph. This will be the box for category with highest value */ { -struct bed *bed = (struct bed *)itemInfo->bed; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; -int i; -double maxExp = 0.0; -int expCount = bed->expCount; -double expScore; -for (i=0; i<expCount; i++) - { - if (!filterCategory(tg, getCategoryName(tg, i))) - continue; - expScore = bed->expScores[i]; - maxExp = max(maxExp, expScore); - } +double maxExp = barChartMaxExpScore(tg, itemInfo); double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_MAX_VIEW_LIMIT, BAR_CHART_MAX_VIEW_LIMIT_DEFAULT); -double maxMedian = ((struct barChartTrack *)tg->extraUiData)->maxMedian; -return valToClippedHeight(maxExp, maxMedian, viewMax, barChartMaxHeight(), extras->doLogTransform); +double maxMedian = extras->maxMedian; +return valToClippedHeight(maxExp, maxMedian, viewMax, extras); } static void drawGraphBox(struct track *tg, struct barChartItem *itemInfo, struct hvGfx *hvg, int x, int y) /* Draw white background for graph */ { Color lighterGray = MAKECOLOR_32(0xF3, 0xF3, 0xF3); int width = barChartWidth(tg, itemInfo); int height = barChartHeight(tg, itemInfo); hvGfxOutlinedBox(hvg, x, y-height, width, height, MG_WHITE, lighterGray); } static void drawGraphBase(struct track *tg, struct barChartItem *itemInfo, struct hvGfx *hvg, int x, int y) /* Draw faint line under graph to delineate extent when bars are missing (category w/ 0 value) */ { Color lightGray = MAKECOLOR_32(0xD1, 0xD1, 0xD1); @@ -490,77 +534,76 @@ if (!extras->noWhiteout) drawGraphBox(tg, itemInfo, hvg, keepX, yZero+1); struct rgbColor lineColor = {.r=0}; int lineColorIx = hvGfxFindColorIx(hvg, lineColor.r, lineColor.g, lineColor.b); int barWidth = barChartBarWidth(tg); int graphPadding = barChartPadding(); char *colorScheme = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_COLORS, BAR_CHART_COLORS_DEFAULT); Color clipColor = MG_MAGENTA; // draw bar graph double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_MAX_VIEW_LIMIT, BAR_CHART_MAX_VIEW_LIMIT_DEFAULT); -double maxMedian = ((struct barChartTrack *)tg->extraUiData)->maxMedian; +double maxMedian = extras->maxMedian; int i; int expCount = bed->expCount; struct barChartCategory *categ; for (i=0, categ=extras->categories; i<expCount && categ != NULL; i++, categ=categ->next) { if (!filterCategory(tg, categ->name)) continue; struct rgbColor fillColor = extras->colors[i]; int fillColorIx = hvGfxFindColorIx(hvg, fillColor.r, fillColor.g, fillColor.b); double expScore = bed->expScores[i]; - int height = valToClippedHeight(expScore, maxMedian, viewMax, - barChartMaxHeight(), extras->doLogTransform); + int height = valToClippedHeight(expScore, maxMedian, viewMax, extras); if (graphPadding == 0 || sameString(colorScheme, BAR_CHART_COLORS_USER)) hvGfxBox(hvg, x1, yZero-height+1, barWidth, height, fillColorIx); else hvGfxOutlinedBox(hvg, x1, yZero-height+1, barWidth, height, fillColorIx, lineColorIx); // mark clipped bar with magenta tip - if (!extras->doLogTransform && expScore > viewMax) + if (barChartUseViewLimit(extras) && expScore > viewMax) hvGfxBox(hvg, x1, yZero-height+1, barWidth, 2, clipColor); x1 = x1 + barWidth + graphPadding; } } static int barChartItemHeightOptionalMax(struct track *tg, void *item, boolean isMax) { // It seems that this can be called early or late enum trackVisibility vis = tg->visibility; if (tg->limitedVisSet) vis = tg->limitedVis; int height; if (vis == tvSquish || vis == tvDense) { if (vis == tvSquish) { tg->lineHeight = barChartSquishItemHeight(); tg->heightPer = tg->lineHeight; } height = tgFixedItemHeight(tg, item); return height; } struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; if (isMax) { int extra = 0; - height= barChartMaxHeight() + barChartMargin() + barChartModelHeight(extras) + extra; + height = extras->maxHeight + barChartMargin() + barChartModelHeight(extras) + extra; return height; } if (item == NULL) return 0; struct barChartItem *itemInfo = (struct barChartItem *)item; if (itemInfo->height != 0) { return itemInfo->height; } int topGraphHeight = barChartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); int bottomGraphHeight = 0; height = topGraphHeight + bottomGraphHeight + barChartMargin() + barChartModelHeight(extras); @@ -651,49 +694,48 @@ int labelWidth = mgFontStringWidth(tl.font, itemName); getItemX(start, end, &x1, &x2); if (x1-labelWidth <= insideX) labelWidth = 0; // map over label int itemHeight = itemInfo->height; mapBoxHc(hvg, itemStart, itemEnd, x1-labelWidth, y, labelWidth, itemHeight-3, tg->track, mapItemName, itemName); // add maps to category bars struct barChartCategory *categs = getCategories(tg); struct barChartCategory *categ = NULL; int barWidth = barChartBarWidth(tg); int padding = barChartPadding(); -double maxMedian = ((struct barChartTrack *)tg->extraUiData)->maxMedian; +double maxMedian = extras->maxMedian; int graphX = barChartX(bed); if (graphX < 0) return; // x1 is at left of graph x1 = insideX + graphX; double viewMax = (double)cartUsualIntClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_MAX_VIEW_LIMIT, BAR_CHART_MAX_VIEW_LIMIT_DEFAULT); int i = 0; for (categ = categs; categ != NULL; categ = categ->next, i++) { if (!filterCategory(tg, categ->name)) continue; double expScore = bed->expScores[i]; - int height = valToClippedHeight(expScore, maxMedian, viewMax, - barChartMaxHeight(), extras->doLogTransform); + int height = valToClippedHeight(expScore, maxMedian, viewMax, extras); mapBoxHc(hvg, itemStart, itemEnd, x1, yZero-height, barWidth, height, tg->track, mapItemName, barChartMapText(tg, categ, expScore)); x1 = x1 + barWidth + padding; } // map over background of chart int graphWidth = barChartWidth(tg, itemInfo); getItemX(start, end, &x1, &x2); mapBoxHc(hvg, itemStart, itemEnd, x1, y, graphWidth, itemHeight-3, tg->track, mapItemName, itemName); } /* This is lifted nearly wholesale from gtexGene track. Could be shared */ static int getBarChartHeight(void *item) @@ -722,31 +764,31 @@ // for visibility, set larger than the usual squish, which is half font height heightPer = barChartSquishItemHeight() * 2; // the squish packer halves this lineHeight=heightPer+1; } else if ((vis == tvPack) || (vis == tvFull)) { heightPer = tl.fontHeight; lineHeight=heightPer; } height = tgFixedTotalHeightOptionalOverflow(tg, vis, lineHeight, heightPer, FALSE); if ((vis == tvPack) || (vis == tvFull)) { // set variable height rows - if (tg->ss && tg->ss->rowCount) + if (tg->ss->rowCount != 0) { if (!tg->ss->rowSizes) { // collect the rowSizes data across all windows assert(currentWindow==windows); // first window assert(tg->ss->vis == vis); // viz matches, we have the right one struct spaceSaver *ssHold; AllocVar(ssHold); struct track *tgSave = tg; for(tg=tgSave; tg; tg=tg->nextWindow) { assert(tgSave->ss->vis == tg->ss->vis); // viz matches, we have the right one spaceSaverSetRowHeights(tg->ss, ssHold, getBarChartHeight); } // share the rowSizes data across all windows