UNPKG

ucsc-xena-client

Version:

UCSC Xena Client. Functional genomics visualizations.

341 lines (290 loc) 11.5 kB
'use strict'; // color scale variants var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } var _ = require('underscore'); var _require = require('./color_helper'), rgb = _require.rgb, RGBToHex = _require.RGBToHex, RGBtoHSV = _require.RGBtoHSV, HSVtoRGB = _require.HSVtoRGB; // d3_category20, replace #7f7f7f gray (that aliases with our N/A gray of #808080) with dark grey #434348 var categoryMore = ["#1f77b4", // dark blue // "#17becf", // dark blue-green "#d62728", // dark red "#9467bd", // dark purple "#ff7f0e", // dark orange "#8c564b", // dark brown "#e377c2", // dark pink "#2ca02c", // dark green "#bcbd22", // dark mustard // "#434348", // very dark grey "#aec7e8", // light blue // "#9edae5", // light blue-green "#dbdb8d", // light mustard "#ff9896", // light salmon "#c5b0d5", // light lavender "#ffbb78", // light orange "#c49c94", // light tan "#f7b6d2", // light pink "#98df8a"]; var round = Math.round; function linearColorScale(domain, range) { function scale(v) { var i = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var _repeat = true; var _v, _i; while (_repeat) { _repeat = false; if (v < domain[i]) { if (i === 0) { return range[0]; } var dx = v - domain[i - 1], x = domain[i] - domain[i - 1]; return [round(dx * (range[i][0] - range[i - 1][0]) / x + range[i - 1][0]), round(dx * (range[i][1] - range[i - 1][1]) / x + range[i - 1][1]), round(dx * (range[i][2] - range[i - 1][2]) / x + range[i - 1][2])]; } if (i === domain.length - 1) { return range[domain.length - 1]; } _v = v; _i = i + 1; v = _v; i = _i; _repeat = true; continue; } } return scale; } // for now, only support one range var log2ColorScale = function log2ColorScale(_ref, _ref2) { var _ref4 = _slicedToArray(_ref, 2), d0 = _ref4[0], d1 = _ref4[1]; var _ref3 = _slicedToArray(_ref2, 2), r0 = _ref3[0], r1 = _ref3[1]; var mb = _.mmap(r0, r1, function (c0, c1) { var ld0 = Math.log2(d0), ld1 = Math.log2(d1), m = (c1 - c0) / (ld1 - ld0), b = c1 - m * ld1; return { m: m, b: b }; }); return function (v) { if (v < d0) { return r0; } if (v > d1) { return r1; } return [round(mb[0].m * Math.log2(v) + mb[0].b), round(mb[1].m * Math.log2(v) + mb[1].b), round(mb[2].m * Math.log2(v) + mb[2].b)]; }; }; // This behaves like a d3 scale, in that it provides range() and domain(). // It additionally provides rgb(), which projects to an rgb triple, instead // of a color string, and rgbRange(), which returns the range as rgb triples. // It doesn't not support other d3 scale methods. var createScale = function createScale(scaleFn, domain, strRange) { var range = strRange.map(rgb), scale = scaleFn(domain, range), rgbFn = function rgbFn(v) { return v == null ? v : scale(v); }, fn = function fn(v) { var rgb = rgbFn(v); return rgb ? RGBToHex.apply(undefined, _toConsumableArray(rgb)) : rgb; }; fn.range = function () { return strRange; }; fn.domain = function () { return domain; }; fn.rgbRange = function () { return range; }; fn.rgb = rgbFn; return fn; }; var setPrecision = function setPrecision(x) { return parseFloat(x.toPrecision(2)); }; var scaleFloatSingle = function scaleFloatSingle(low, high, min, max) { return createScale(linearColorScale, [min, max], [low, high]); }; var scaleFloatThresholdNegative = function scaleFloatThresholdNegative(low, zero, min, thresh) { return createScale(linearColorScale, _.map([min, thresh], setPrecision), [low, zero]); }; var scaleFloatThresholdPositive = function scaleFloatThresholdPositive(zero, high, thresh, max) { return createScale(linearColorScale, _.map([thresh, max], setPrecision), [zero, high]); }; var scaleFloatThreshold = function scaleFloatThreshold(low, zero, high, min, minThresh, maxThresh, max) { return createScale(linearColorScale, _.map([min, minThresh, maxThresh, max], setPrecision), [low, zero, zero, high]); }; var scaleFloatThresholdLogNegative = function scaleFloatThresholdLogNegative(low, zero, min, thresh) { return createScale(log2ColorScale, _.map([min, thresh], setPrecision), [low, zero]); }; var scaleFloatThresholdLogPositive = function scaleFloatThresholdLogPositive(zero, high, thresh, max) { return createScale(log2ColorScale, _.map([thresh, max], setPrecision), [zero, high]); }; var scaleFloatLog = function scaleFloatLog(low, high, min, max) { return createScale(log2ColorScale, _.map([min, max], setPrecision), [low, high]); }; //var ordinal = (count, custom) => d3.scaleOrdinal().range(custom || categoryMore).domain(_.range(count)); // d3 ordinal scales will de-dup the domain using an incredibly slow algorithm. var _ordinal = function _ordinal(count, scale) { scale = scale || categoryMore; // handle null return function (v) { return scale[v % scale.length]; }; }; function scaleFloatDouble(low, zero, high, min, max) { var absmax = Math.max(-min, max); return createScale(linearColorScale, [-absmax, 0, absmax], [low, zero, high]); } var clip = function clip(min, max, x) { return x < min ? min : x > max ? max : x; }; var rgbToArray = function rgbToArray(obj) { return [obj.r, obj.g, obj.b]; }; // Find the minimum path from h0 to h1 in the hue space, which // wraps at 1. function minHueRange(h0, h1) { var _ref5 = h0 < h1 ? [h0, h1] : [h1, h0], _ref6 = _slicedToArray(_ref5, 2), low = _ref6[0], high = _ref6[1]; return high - low > low + 1 - high ? [high, low + 1] : [low, high]; } function scaleTrendAmplitude(low, zero, high, origin, thresh, max) { var _minHueRange = minHueRange(RGBtoHSV.apply(undefined, _toConsumableArray(rgb(low))).h, RGBtoHSV.apply(undefined, _toConsumableArray(rgb(high))).h), _minHueRange2 = _slicedToArray(_minHueRange, 2), h0 = _minHueRange2[0], h1 = _minHueRange2[1]; return { // trend is [0, 1], representing net amplification vs. deletion. // power is [0, dataMax], representing avg. distance from zero point. lookup: function lookup(trend, power) { if (power == null) { return [128, 128, 128]; } var h = clip(h0, h1, h0 + trend * (h1 - h0)); var s = clip(0, 1, (power - thresh) / (max - origin - thresh)); return rgbToArray(HSVtoRGB(h, s, 1)); } }; } // https://stackoverflow.com/questions/7251872/is-there-a-better-color-scale-than-the-rainbow-colormap var isoStops = [[0.847, 0.057, 0.057], [0.527, 0.527, 0], [0, 0.592, 0], [0, 0.559, 0.559], [0.316, 0.316, 0.991], [0.718, 0, 0.718]].map(function (fr) { return fr.map(function (v) { return 255 * v; }); }); function isoluminant(low, high) { var count = isoStops.length, stop = (high - low) / count, clip = function clip(i) { return i < 0 ? 0 : i > count - 1 ? count - 1 : i; }; return function (v) { var p = clip((v - low) / stop), h = isoStops[Math.ceil(p)], li = Math.floor(p), l = isoStops[li], f = p - li; return 'rgb(' + ~~(l[0] * (1 - f) + h[0] * f) + ',' + ~~(l[1] * (1 - f) + h[1] * f) + ',' + ~~(l[2] * (1 - f) + h[2] * f) + ')'; }; } // A scale for when we have no data. Implements the scale API // so we don't have to put a bunch of special cases in the drawing code. var noDataScale = function noDataScale() { return "gray"; }; noDataScale.domain = function () { return []; }; var _colorScale = { 'no-data': function noData() { return noDataScale; }, 'float-pos': function floatPos(__) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return scaleFloatSingle.apply(undefined, args); }, 'float-neg': function floatNeg(__) { for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } return scaleFloatSingle.apply(undefined, args); }, 'float': function float(__) { for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { args[_key3 - 1] = arguments[_key3]; } return scaleFloatDouble.apply(undefined, args); }, 'float-thresh-pos': function floatThreshPos(__) { for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } return scaleFloatThresholdPositive.apply(undefined, args); }, 'float-thresh-neg': function floatThreshNeg(__) { for (var _len5 = arguments.length, args = Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { args[_key5 - 1] = arguments[_key5]; } return scaleFloatThresholdNegative.apply(undefined, args); }, 'float-thresh': function floatThresh(__) { for (var _len6 = arguments.length, args = Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { args[_key6 - 1] = arguments[_key6]; } return scaleFloatThreshold.apply(undefined, args); }, 'float-thresh-log-pos': function floatThreshLogPos(__) { for (var _len7 = arguments.length, args = Array(_len7 > 1 ? _len7 - 1 : 0), _key7 = 1; _key7 < _len7; _key7++) { args[_key7 - 1] = arguments[_key7]; } return scaleFloatThresholdLogPositive.apply(undefined, args); }, 'float-thresh-log-neg': function floatThreshLogNeg(__) { for (var _len8 = arguments.length, args = Array(_len8 > 1 ? _len8 - 1 : 0), _key8 = 1; _key8 < _len8; _key8++) { args[_key8 - 1] = arguments[_key8]; } return scaleFloatThresholdLogNegative.apply(undefined, args); }, 'float-log': function floatLog(__) { for (var _len9 = arguments.length, args = Array(_len9 > 1 ? _len9 - 1 : 0), _key9 = 1; _key9 < _len9; _key9++) { args[_key9 - 1] = arguments[_key9]; } return scaleFloatLog.apply(undefined, args); }, 'trend-amplitude': function trendAmplitude(__) { for (var _len10 = arguments.length, args = Array(_len10 > 1 ? _len10 - 1 : 0), _key10 = 1; _key10 < _len10; _key10++) { args[_key10 - 1] = arguments[_key10]; } return scaleTrendAmplitude.apply(undefined, args); }, 'ordinal': function ordinal(__) { for (var _len11 = arguments.length, args = Array(_len11 > 1 ? _len11 - 1 : 0), _key11 = 1; _key11 < _len11; _key11++) { args[_key11 - 1] = arguments[_key11]; } return _ordinal.apply(undefined, args); } }; module.exports = { colorScale: function colorScale(_ref7) { var _ref8 = _toArray(_ref7), type = _ref8[0], args = _ref8.slice(1); return _colorScale[type].apply(_colorScale, [type].concat(_toConsumableArray(args))); }, categoryMore: categoryMore, isoluminant: isoluminant };