a4adfa326bf8bec73020ee1a8aa0cb6251ea8663 tdreszer Thu Jul 24 11:20:39 2014 -0700 Checking in changes to make 'spectrum' (aka 'useScore') actually work on any pair of color/altColor settings. It used to be that only shadesOfGray, shadesOfBrown and shadesOfSea worked, leaving most cases to be shadesOfGray. I have sat on these changes for months. I am also checking in ifdef'd out code to calc the shade on the fly, rather than relaying on track->colorShades set of precomputed 10 shades. The only reason this code is not used is that it would require changing many many places/uses of colorShades. Since I am bein laid off, it seems inappropriate to make this more dramatic change at this time. diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c index 26abd58..7a32cdf 100644 --- src/hg/hgTracks/simpleTracks.c +++ src/hg/hgTracks/simpleTracks.c @@ -2414,32 +2414,32 @@ hvGfxFindColorIx(hvg, itemRgb.r, itemRgb.g, itemRgb.b); } else *retColor = *retBarbColor = lf->filterColor; } else if (tg->itemColor) { *retColor = tg->itemColor(tg, lf, hvg); *retBarbColor = tg->ixAltColor; } else if (tg->colorShades) { boolean isXeno = (tg->subType == lfSubXeno) || (tg->subType == lfSubChain) || startsWith("mrnaBla", tg->table); - *retColor = tg->colorShades[lf->grayIx+isXeno]; - *retBarbColor = tg->colorShades[lf->grayIx]; + *retColor = colorBySpectrumOrDefault(hvg,tg,lf->grayIx+isXeno,*retColor); + *retBarbColor = colorBySpectrumOrDefault(hvg,tg,lf->grayIx, *retBarbColor); } else { *retColor = tg->ixColor; *retBarbColor = tg->ixAltColor; } } Color linkedFeaturesNameColor(struct track *tg, void *item, struct hvGfx *hvg) /* Determine the color of the name for the linked feature. */ { Color col, barbCol; lfColors(tg, item, hvg, &col, &barbCol); return col; } @@ -5063,35 +5063,35 @@ tg->drawItemAt = gadDrawAt; tg->mapItem = bedPlusLabelMapItem; tg->nextPrevExon = simpleBedNextPrevEdge; } void rgdQtlDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw a single rgdQtl item at position. */ { struct bed *bed = item; struct trackDb *tdb = tg->tdb; if (tg->itemColor != NULL) color = tg->itemColor(tg, bed, hvg); -else if (tg->colorShades) +else { int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); - color = tg->colorShades[grayInRange(bed->score, scoreMin, scoreMax)]; + color = colorBySpectrumOrDefault(hvg, tg, grayInRange(bed->score, scoreMin, scoreMax),color); } if (color) { int heightPer = tg->heightPer; int s = max(bed->chromStart, winStart), e = min(bed->chromEnd, winEnd); if (s > e) return; int x1 = round((s-winStart)*scale) + xOff; int x2 = round((e-winStart)*scale) + xOff; int w = x2 - x1; if (w < 1) w = 1; hvGfxBox(hvg, x1, y, w, heightPer, color); if (tg->drawName && vis != tvSquish) { @@ -6179,34 +6179,31 @@ //boolean thickDrawItem = (trackDbSetting(tdb, "thickDrawItem") != NULL); int colors[8] = { orangeColor, greenColor, blueColor, brickColor, darkBlueColor, darkGreenColor, 1, MG_RED} ; if (tg->itemColor != NULL) color = tg->itemColor(tg, bed, hvg); else - { - if (tg->colorShades) - color = tg->colorShades[grayInRange(bed->score, scoreMin, scoreMax)]; - } + color = colorBySpectrumOrDefault(hvg,tg,grayInRange(bed->score, scoreMin, scoreMax),color); w = x2-x1; if (w < 1) w = 1; //if (color) { int ii; for(ii=0; ii < 8; ii++) { if (bed->score & (1 << ii)) hvGfxBox(hvg, x1, y+(ii*6), w, 6, colors[ii]); } if (tg->drawName && vis != tvSquish) { @@ -7522,31 +7519,31 @@ { return getSeqColorDefault(seqName, hvg, chromColor[0]); } Color lfChromColor(struct track *tg, void *item, struct hvGfx *hvg) /* Return color of chromosome for linked feature type items * where the chromosome is listed somewhere in the lf->name. */ { struct linkedFeatures *lf = item; return getSeqColorDefault(lf->name, hvg, tg->ixColor); } Color interactionColor(struct track *tg, void *item, struct hvGfx *hvg) { struct linkedFeatures *lf = item; -return tg->colorShades[lf->grayIx]; +return colorBySpectrumOrDefault(hvg,tg,lf->grayIx,hvGfxFindRgb(hvg, &tg->color)); #ifdef NOTNOW // leaving this in the code in case we want chrom color again char *name = tg->itemName(tg, item); struct linkedFeatures *lf = item; if (slCount(lf->components) == 2) return MG_BLACK; return getSeqColorDefault(name, hvg, tg->ixColor); #endif } void interactionLeftLabels(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) @@ -9175,30 +9172,125 @@ if (!subtrack->limitedVisSet) { if (isSubtrackVisible(subtrack)) subtrack->visibility = track->visibility; else subtrack->visibility = tvHide; } } boolean colorsSame(struct rgbColor *a, struct rgbColor *b) /* Return true if two colors are the same. */ { return a->r == b->r && a->g == b->g && a->b == b->b; } +//#define SPECTRUM_LIVE +#ifdef SPECTRUM_LIVE +// Define SPECTRUM_LIVE to enable live calculation of color rather than using track->colorShades +// History: trackDb setting 'spectrum' (aka 'useScore') is defined to use an item's score to +// determine color on a spectrum. This was originally limited to gray scale and only +// the discrete 10 shades defined in the colorShades array. Two additional color scales +// were added (shadesOfBrown and shadesOfSea, but with no documentation on how a trackDb +// user could make use of these. The array of shades has been widely used on track types +// beyond beds. +// Spectrum support has been added to simply fill in the colorShades array on first use, based +// upon color/altColor. This simple change does not need to touch on ubiquitous code that +// relies upon colorShades. It is a targeted change with limited footprint, but maintains +// a hash of spectrums that gets built as needed. +// Live spectrum, on the other hand would do away with colorShades and calculate the color whenever +// needed. There would no longer be a limit to 10 shades, but would require extensive +// code changes to eliminate dependence upon the colorShades array. There is no +// compelling efficiency of one method versus another. Using live calculation should +// ultimately simplify code and structures. But as I am being laid off, I feel it would +// be irresponsible to check in a larger change at this time +// TODO: move to hvGfx.c +Color hvGfxColorInSpectrum(struct hvGfx *hvg, struct rgbColor *colorOfZero, + struct rgbColor *colorOfOne, float aboveZero) +// Returns the color that lies between zero and one in the spectrum. +{ +assert(aboveZero >= 0 && aboveZero <= 1.0); +float belowOne = 1.0 - aboveZero; +int red = (belowOne * colorOfZero->r) + (aboveZero * colorOfOne->r); +int green = (belowOne * colorOfZero->g) + (aboveZero * colorOfOne->g); +int blue = (belowOne * colorOfZero->b) + (aboveZero * colorOfOne->b); +return hvGfxFindColorIx(hvg, red, green, blue); +} + +Color colorWithinRange(struct hvGfx *hvg, struct rgbColor *bottomColor, struct rgbColor *topColor, + int value, int rangeBottom, int rangeTop) +// Returns color within range. Colors outside of range will top or bottom out. +// If range not defined, then range defaults to normal score range (0-1000). +{ +// requested spectrum must have 2 colors +if (!colorsSame(bottomColor, topColor)) + { + if (rangeBottom >= rangeTop) + { + rangeBottom = 0; // default this to normal score range + rangeTop = 1000; + } + if (value < rangeBottom) + value = rangeBottom; + if (value > rangeTop) + value = rangeTop; + float aboveZero = ((float)value - rangeBottom)/(rangeTop - rangeBottom); + return hvGfxColorInSpectrum(hvg, bottomColor, topColor,aboveZero); + } +else // no color range so use the only color + return hvGfxFindColorIx(hvg, topColor->r, topColor->g, topColor->b); +} +#endif//def SPECTRUM_LIVE + +Color colorBySpectrumOrDefault(struct hvGfx *hvg, struct track *track,int shade,Color defaultColor) +// Returns color to use if spectrum exists, else returns default +{ +if (track->tdb->useScore && track->colorShades == NULL) + { +#ifdef SPECTRUM_LIVE + return colorWithinTrackIxRange(hvg,track,shade); +#else// ifndef SPECTRUM_LIVE + struct rgbColor paintItBlack = {0, 0, 0}; + if (!colorsSame(&paintItBlack, &track->color) + || !colorsSame(&paintItBlack, &track->altColor)) + { + // Caching of the spectrum may be overkill, but if there are hundreds, this will be helpful + // An slPair list would be more space efficient, but with slower lookups + static struct hash *spectrumHash = NULL; + char spectrumName[32]; + safef(spectrumName,32,"%02X%02X%02X,%02X%02X%02X", + track->color.r, track->color.g, track->color.b, + track->altColor.r,track->altColor.g,track->altColor.b); + if (spectrumHash == NULL) + spectrumHash = hashNew(4); // small hash + else + track->colorShades = hashFindVal(spectrumHash,spectrumName); + if (track->colorShades == NULL) + { + track->colorShades = needMem(sizeof(Color *) * maxShade + 2); + hvGfxMakeColorGradient(hvg, &track->color, &track->altColor, + maxShade+1, track->colorShades); + hashAddUnique(spectrumHash,spectrumName,track->colorShades); + } + } +#endif//ndef SPECTRUM_LIVE + } +if (track->colorShades == NULL || shade > maxShade + 1) // true range is 1 to 10. + return defaultColor; +return track->colorShades[shade]; +} + #ifndef GBROWSE void loadValAl(struct track *tg) /* Load the items in one custom track - just move beds in * window... */ { struct linkedFeatures *lfList = NULL, *lf; struct bed *bed, *list = NULL; struct sqlConnection *conn = hAllocConn(database); struct sqlResult *sr; char **row; int rowOffset; sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset); while ((row = sqlNextRow(sr)) != NULL) { @@ -9841,34 +9933,31 @@ MgFont *font, Color color, enum trackVisibility vis) /* Draw a right- or left-pointing triangle at position. * If item has width > 1 or block/cds structure, those will be ignored -- * this only draws a triangle (direction depending on strand). */ { struct bed *bed = item; int x1 = round((double)((int)bed->chromStart-winStart)*scale) + xOff; int y2 = y + tg->heightPer-1; struct trackDb *tdb = tg->tdb; int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0")); int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000")); if (tg->itemColor != NULL) color = tg->itemColor(tg, bed, hvg); else - { - if (tg->colorShades) - color = tg->colorShades[grayInRange(bed->score, scoreMin, scoreMax)]; - } + color = colorBySpectrumOrDefault(hvg,tg,grayInRange(bed->score, scoreMin, scoreMax),color); drawTri(hvg, x1, y, y2, color, bed->strand[0]); } void simpleBedTriangleMethods(struct track *tg) /* Load up simple bed features methods, but use triangleDrawAt. */ { bedMethods(tg); tg->drawItemAt = triangleDrawAt; } void loadColoredExonBed(struct track *tg) /* Load the items into a linkedFeaturesSeries. */ { struct sqlConnection *conn = hAllocConn(database); @@ -12359,119 +12448,86 @@ } track->height = height; return track->height; } int trackPriCmp(const void *va, const void *vb) /* Compare for sort based on priority */ { const struct track *a = *((struct track **)va); const struct track *b = *((struct track **)vb); return (a->priority - b->priority); } void makeCompositeTrack(struct track *track, struct trackDb *tdb) -/* Construct track subtrack list from trackDb entry. - * Sets up color gradient in subtracks if requested */ +// Construct track subtrack list from trackDb entry. { -unsigned char finalR = track->color.r, finalG = track->color.g, - finalB = track->color.b; -unsigned char altR = track->altColor.r, altG = track->altColor.g, - altB = track->altColor.b; -unsigned char deltaR = 0, deltaG = 0, deltaB = 0; - struct slRef *tdbRef, *tdbRefList = trackDbListGetRefsToDescendantLeaves(tdb->subtracks); struct trackDb *subTdb; int subCount = slCount(tdbRefList); -int altColors = subCount - 1; struct track *subtrack = NULL; TrackHandler handler; boolean smart = FALSE; /* ignore if no subtracks */ if (!subCount) return; char *compositeTrack = trackDbLocalSetting(tdb, "compositeTrack"); /* look out for tracks that manage their own subtracks */ if (startsWith("wig", tdb->type) || startsWith("bedGraph", tdb->type) || (compositeTrack != NULL && rStringIn("smart", compositeTrack))) smart = TRUE; /* setup function handlers for composite track */ handler = lookupTrackHandlerClosestToHome(tdb); if (smart && handler != NULL) /* handles it's own load and height */ handler(track); else { track->loadItems = compositeLoad; track->totalHeight = compositeTotalHeight; } -if (altColors && (finalR || finalG || finalB)) - { - /* not black -- make a color gradient for the subtracks, - from black, to the specified color */ - deltaR = (finalR - altR) / altColors; - deltaG = (finalG - altG) / altColors; - deltaB = (finalB - altB) / altColors; - } - /* fill in subtracks of composite track */ for (tdbRef = tdbRefList; tdbRef != NULL; tdbRef = tdbRef->next) { subTdb = tdbRef->val; subtrack = trackFromTrackDb(subTdb); handler = lookupTrackHandlerClosestToHome(subTdb); if (handler != NULL) handler(subtrack); /* Add subtrack settings (table, colors, labels, vis & pri). This is only * needed in the "not noInherit" case that hopefully will go away soon. */ subtrack->track = subTdb->track; subtrack->table = subTdb->table; subtrack->shortLabel = subTdb->shortLabel; subtrack->longLabel = subTdb->longLabel; subtrack->priority = subTdb->priority; subtrack->parent = track; - /* Add color gradient. */ - if (finalR || finalG || finalB) - { - subtrack->color.r = altR; - subtrack->altColor.r = (255+altR)/2; - altR += deltaR; - subtrack->color.g = altG; - subtrack->altColor.g = (255+altG)/2; - altG += deltaG; - subtrack->color.b = altB; - subtrack->altColor.b = (255+altB)/2; - altB += deltaB; - } - else - { subtrack->color.r = subTdb->colorR; subtrack->color.g = subTdb->colorG; subtrack->color.b = subTdb->colorB; subtrack->altColor.r = subTdb->altColorR; subtrack->altColor.g = subTdb->altColorG; subtrack->altColor.b = subTdb->altColorB; - } slAddHead(&track->subtracks, subtrack); } slSort(&track->subtracks, trackPriCmp); } struct track *trackFromTrackDb(struct trackDb *tdb) /* Create a track based on the tdb */ { struct track *track = NULL; char *exonArrows; char *nextItem; if (!tdb) return NULL; track = trackNew(); @@ -12508,34 +12564,38 @@ track->lineHeight = tl.fontHeight+1; track->heightPer = track->lineHeight - 1; track->private = tdb->private; track->defaultPriority = tdb->priority; char lookUpName[256]; safef(lookUpName, sizeof(lookUpName), "%s.priority", tdb->track); tdb->priority = cartUsualDouble(cart, lookUpName, tdb->priority); track->priority = tdb->priority; track->groupName = cloneString(tdb->grp); /* save default priority and group so we can reset it later */ track->defaultGroupName = cloneString(tdb->grp); track->canPack = tdb->canPack; if (tdb->useScore) { /* Todo: expand spectrum opportunities. */ + struct rgbColor paintItBlack = {0, 0, 0}; if (colorsSame(&brownColor, &track->color)) track->colorShades = shadesOfBrown; else if (colorsSame(&darkSeaColor, &track->color)) track->colorShades = shadesOfSea; + else if (!colorsSame(&paintItBlack, &track->color) + || !colorsSame(&paintItBlack, &track->altColor)) + track->colorShades = NULL; // Signal to generate spectrum on first need else track->colorShades = shadesOfGray; } track->tdb = tdb; /* Handle remote database settings - just a JK experiment at the moment. */ track->remoteSqlHost = trackDbSetting(tdb, "sqlHost"); track->remoteSqlUser = trackDbSetting(tdb, "sqlUser"); track->remoteSqlPassword = trackDbSetting(tdb, "sqlPassword"); track->remoteSqlDatabase = trackDbSetting(tdb, "sqlDatabase"); track->remoteSqlTable = trackDbSetting(tdb, "sqlTable"); track->isRemoteSql = (track->remoteSqlHost != NULL && track->remoteSqlUser != NULL && track->remoteSqlDatabase != NULL && track->remoteSqlTable !=NULL); exonArrows = trackDbSetting(tdb, "exonArrows");