Skip to content

Commit

Permalink
implement crossTraceDefaults, calc and Colobar.draw for color axes
Browse files Browse the repository at this point in the history
- use min of min (and max of max) of all traces linked to same
  color axis to compute min/max when auto:true
- call Colorscale.crossTraceDefaults after having relinked layout,
  to use coloraxis._min, coloraxis._max
-
  • Loading branch information
etpinard committed Apr 24, 2019
1 parent 8f1fde1 commit 5bb341e
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 95 deletions.
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 : '');
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)) {
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

0 comments on commit 5bb341e

Please sign in to comment.