ucsc-xena-client
Version:
UCSC Xena Client. Functional genomics visualizations.
341 lines (290 loc) • 11.5 kB
JavaScript
// 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
};
;