Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shared color axes #3803

Merged
merged 14 commits into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 66 additions & 37 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,67 +72,96 @@ function draw(gd) {
}

function makeColorBarData(gd) {
var fullLayout = gd._fullLayout;
var calcdata = gd.calcdata;
var out = [];

// single out item
var opts;
// colorbar attr parent container
var cont;
// trace attr container
var trace;
// colorbar options
var cbOpt;

function initOpts(opts) {
return extendFlat(opts, {
// fillcolor can be a d3 scale, domain is z values, range is colors
// or leave it out for no fill,
// or set to a string constant for single-color fill
_fillcolor: null,
// line.color has the same options as fillcolor
_line: {color: null, width: null, dash: null},
// levels of lines to draw.
// note that this DOES NOT determine the extent of the bar
// that's given by the domain of fillcolor
// (or line.color if no fillcolor domain)
_levels: {start: null, end: null, size: null},
// separate fill levels (for example, heatmap coloring of a
// contour map) if this is omitted, fillcolors will be
// evaluated halfway between levels
_filllevels: null,
// for continuous colorscales: fill with a gradient instead of explicit levels
// value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
_fillgradient: null,
// when using a gradient, we need the data range specified separately
_zrange: null
});
}

function calcOpts() {
if(typeof cbOpt.calc === 'function') {
cbOpt.calc(gd, trace, opts);
} else {
opts._fillgradient = cont.reversescale ?
flipScale(cont.colorscale) :
cont.colorscale;
opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]];
}
}

for(var i = 0; i < calcdata.length; i++) {
var cd = calcdata[i];
var trace = cd[0].trace;
trace = cd[0].trace;
var moduleOpts = trace._module.colorbar;

if(trace.visible === true && moduleOpts) {
var allowsMultiplotCbs = Array.isArray(moduleOpts);
var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts];

for(var j = 0; j < cbOpts.length; j++) {
var cbOpt = cbOpts[j];
cbOpt = cbOpts[j];
var contName = cbOpt.container;
var cont = contName ? trace[contName] : trace;
cont = contName ? trace[contName] : trace;

if(cont && cont.showscale) {
var opts = cont.colorbar;
opts = initOpts(cont.colorbar);
opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : '');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commits essentially resolves #3555

I didn't add support for marker.line. colorbars (yet) - let me know you think that's a good idea.

opts._traceIndex = trace.index;
opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.';

extendFlat(opts, {
// fillcolor can be a d3 scale, domain is z values, range is colors
// or leave it out for no fill,
// or set to a string constant for single-color fill
_fillcolor: null,
// line.color has the same options as fillcolor
_line: {color: null, width: null, dash: null},
// levels of lines to draw.
// note that this DOES NOT determine the extent of the bar
// that's given by the domain of fillcolor
// (or line.color if no fillcolor domain)
_levels: {start: null, end: null, size: null},
// separate fill levels (for example, heatmap coloring of a
// contour map) if this is omitted, fillcolors will be
// evaluated halfway between levels
_filllevels: null,
// for continuous colorscales: fill with a gradient instead of explicit levels
// value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
_fillgradient: null,
// when using a gradient, we need the data range specified separately
_zrange: null
});

if(typeof cbOpt.calc === 'function') {
cbOpt.calc(gd, cd, opts);
} else {
opts._fillgradient = cont.reversescale ?
flipScale(cont.colorscale) :
cont.colorscale;
opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]];
}

calcOpts();
out.push(opts);
}
}
}
}

for(var k in fullLayout._colorAxes) {
cont = fullLayout[k];

if(cont.showscale) {
var colorAxOpts = fullLayout._colorAxes[k];

opts = initOpts(cont.colorbar);
opts._id = 'cb' + k;

cbOpt = {min: 'cmin', max: 'cmax'};
calcOpts();
out.push(opts);
}
}

return out;
}

Expand Down
53 changes: 33 additions & 20 deletions src/components/colorscale/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,50 @@

'use strict';

var isNumeric = require('fast-isnumeric');

var Lib = require('../../lib');
var extractOpts = require('./helpers').extractOpts;

module.exports = function calc(gd, trace, opts) {
var fullLayout = gd._fullLayout;
var vals = opts.vals;
var containerStr = opts.containerStr;
var cLetter = opts.cLetter;

var container = containerStr ?
Lib.nestedProperty(trace, containerStr).get() :
trace;

var autoAttr = cLetter + 'auto';
var minAttr = cLetter + 'min';
var maxAttr = cLetter + 'max';
var midAttr = cLetter + 'mid';
var auto = container[autoAttr];
var min = container[minAttr];
var max = container[maxAttr];
var mid = container[midAttr];
var scl = container.colorscale;
var cOpts = extractOpts(container);
var auto = cOpts.auto !== false;
var min = cOpts.min;
var max = cOpts.max;
var mid = cOpts.mid;

var minVal = function() { return Lib.aggNums(Math.min, null, vals); };
var maxVal = function() { return Lib.aggNums(Math.max, null, vals); };

if(auto !== false || min === undefined) {
min = Lib.aggNums(Math.min, null, vals);
if(min === undefined) {
min = minVal();
} else if(auto) {
if(container._colorAx && isNumeric(min)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we get all traces sharing the same coloraxis to contribute to the auto cmin / cmax computations.

min = Math.min(min, minVal());
} else {
min = minVal();
}
}

if(auto !== false || max === undefined) {
max = Lib.aggNums(Math.max, null, vals);
if(max === undefined) {
max = maxVal();
} else if(auto) {
if(container._colorAx && isNumeric(max)) {
max = Math.max(max, maxVal());
} else {
max = maxVal();
}
}

if(auto !== false && mid !== undefined) {
if(auto && mid !== undefined) {
if(max - mid > mid - min) {
min = mid - (max - mid);
} else if(max - mid < mid - min) {
Expand All @@ -51,14 +64,14 @@ module.exports = function calc(gd, trace, opts) {
max += 0.5;
}

container['_' + minAttr] = container[minAttr] = min;
container['_' + maxAttr] = container[maxAttr] = max;
cOpts._sync('min', min);
cOpts._sync('max', max);

if(container.autocolorscale) {
if(cOpts.autocolorscale) {
var scl;
if(min * max < 0) scl = fullLayout.colorscale.diverging;
else if(min >= 0) scl = fullLayout.colorscale.sequential;
else scl = fullLayout.colorscale.sequentialminus;

container._colorscale = container.colorscale = scl;
cOpts._sync('colorscale', scl);
}
};
54 changes: 30 additions & 24 deletions src/components/colorscale/cross_trace_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,66 @@

var Lib = require('../../lib');
var hasColorscale = require('./helpers').hasColorscale;
var extractOpts = require('./helpers').extractOpts;

module.exports = function crossTraceDefaults(fullData) {
module.exports = function crossTraceDefaults(fullData, fullLayout) {
function replace(cont, k) {
var val = cont['_' + k];
if(val !== undefined) {
cont[k] = val;
}
}

function relinkColorAtts(trace, cAttrs) {
var cont = cAttrs.container ?
Lib.nestedProperty(trace, cAttrs.container).get() :
trace;
function relinkColorAtts(outerCont, cbOpt) {
var cont = cbOpt.container ?
Lib.nestedProperty(outerCont, cbOpt.container).get() :
outerCont;

if(cont) {
var isAuto = cont.zauto || cont.cauto;
var minAttr = cAttrs.min;
var maxAttr = cAttrs.max;
if(cont.coloraxis) {
// stash ref to color axis
cont._colorAx = fullLayout[cont.coloraxis];
} else {
var cOpts = extractOpts(cont);
var isAuto = cOpts.auto;

if(isAuto || cont[minAttr] === undefined) {
replace(cont, minAttr);
}
if(isAuto || cont[maxAttr] === undefined) {
replace(cont, maxAttr);
}
if(cont.autocolorscale) {
replace(cont, 'colorscale');
if(isAuto || cOpts.min === undefined) {
replace(cont, cbOpt.min);
}
if(isAuto || cOpts.max === undefined) {
replace(cont, cbOpt.max);
}
if(cOpts.autocolorscale) {
replace(cont, 'colorscale');
}
}
}
}

for(var i = 0; i < fullData.length; i++) {
var trace = fullData[i];
var colorbar = trace._module.colorbar;
var cbOpts = trace._module.colorbar;

if(colorbar) {
if(Array.isArray(colorbar)) {
for(var j = 0; j < colorbar.length; j++) {
relinkColorAtts(trace, colorbar[j]);
if(cbOpts) {
if(Array.isArray(cbOpts)) {
for(var j = 0; j < cbOpts.length; j++) {
relinkColorAtts(trace, cbOpts[j]);
}
} else {
relinkColorAtts(trace, colorbar);
relinkColorAtts(trace, cbOpts);
}
}

// TODO could generalize _module.colorscale and use it here?

if(hasColorscale(trace, 'marker.line')) {
relinkColorAtts(trace, {
container: 'marker.line',
min: 'cmin',
max: 'cmax'
});
}
}

for(var k in fullLayout._colorAxes) {
relinkColorAtts(fullLayout[k], {min: 'cmin', max: 'cmax'});
}
};
8 changes: 2 additions & 6 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -638,13 +638,9 @@ drawing.tryColorscale = function(marker, prefix) {
var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker;

if(cont) {
var scl = cont.colorscale;
var colorArray = cont.color;

if(scl && Lib.isArrayOrTypedArray(colorArray)) {
return Colorscale.makeColorScaleFunc(
Colorscale.extractScale(cont, {cLetter: 'c'})
);
if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) {
return Colorscale.makeColorScaleFuncFromTrace(cont);
}
}
return Lib.identity;
Expand Down
4 changes: 3 additions & 1 deletion src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,6 @@ plots.supplyDefaults = function(gd, opts) {
for(i = 0; i < crossTraceDefaultsFuncs.length; i++) {
crossTraceDefaultsFuncs[i](newFullData, newFullLayout);
}
Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);

// turn on flag to optimize large splom-only graphs
// mostly by omitting SVG layers during Cartesian.drawFramework
Expand Down Expand Up @@ -487,6 +486,9 @@ plots.supplyDefaults = function(gd, opts) {
// relink functions and _ attributes to promote consistency between plots
relinkPrivateKeys(newFullLayout, oldFullLayout);

// colorscale crossTraceDefaults needs newFullLayout with relinked keys
Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);

// For persisting GUI-driven changes in layout
// _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys
if(!newFullLayout._preGUI) newFullLayout._preGUI = {};
Expand Down
10 changes: 4 additions & 6 deletions src/traces/heatmap/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Fx = require('../../components/fx');
var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');
var extractOpts = require('../../components/colorscale').extractOpts;

module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) {
var cd0 = pointData.cd[0];
Expand All @@ -24,7 +24,6 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay
var xc = cd0.xCenter;
var yc = cd0.yCenter;
var zmask = cd0.zmask;
var range = [trace.zmin, trace.zmax];
var zhoverformat = trace.zhoverformat;
var x2 = x;
var y2 = y;
Expand Down Expand Up @@ -95,17 +94,16 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay
text = cd0.text[ny][nx];
}

var zLabel;
// dummy axis for formatting the z value
var cOpts = extractOpts(trace);
var dummyAx = {
type: 'linear',
range: range,
range: [cOpts.min, cOpts.max],
hoverformat: zhoverformat,
_separators: xa._separators,
_numFormat: xa._numFormat
};
var zLabelObj = Axes.tickText(dummyAx, zVal, 'hover');
zLabel = zLabelObj.text;
var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text;

return [Lib.extendFlat(pointData, {
index: [ny, nx],
Expand Down
Loading