Skip to content

Commit

Permalink
add 'coloraxis' layout attr and default logic
Browse files Browse the repository at this point in the history
- reuse Colorscale.supplyDefaults
- use 'cmin', 'cmax' and 'cauto' no matter the trace types plotted
- keep track of color axes referenced in the traces in
  fullLayout._colorAxes
- ignore coloraxis referenced when colorbar visuals are incompatible
- tweak PlotSchema.get() for layout.colorscale / layout.coloraxis?
  edge cases
  • Loading branch information
etpinard committed Apr 24, 2019
1 parent 5e7ed27 commit ae3f407
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 64 deletions.
19 changes: 19 additions & 0 deletions src/components/colorscale/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
'use strict';

var colorbarAttrs = require('../colorbar/attributes');
var counterRegex = require('../../lib/regex').counter;

var palettes = require('./scales.js').scales;
var paletteStr = Object.keys(palettes);

Expand Down Expand Up @@ -245,5 +247,22 @@ module.exports = function colorScaleAttrs(context, opts) {
attrs.colorbar = colorbarAttrs;
}

if(!opts.noColorAxis) {
attrs.coloraxis = {
valType: 'subplotid',
role: 'info',
regex: counterRegex('coloraxis'),
dflt: null,
editType: 'calc',
description: [
'Sets a reference to a shared color axis.',
'References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc.',
'Settings for these shared color axes are set in the layout, under',
'`layout.coloraxis`, `layout.coloraxis2`, etc.',
'Note that multiple color scales can be linked to the same color axis.'
].join(' ')
};
}

return attrs;
};
59 changes: 51 additions & 8 deletions src/components/colorscale/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,63 @@ var hasColorbar = require('../colorbar/has_colorbar');
var colorbarDefaults = require('../colorbar/defaults');

var isValidScale = require('./scales').isValid;
var traceIs = require('../../registry').traceIs;

function npMaybe(cont, prefix) {
function npMaybe(outerCont, prefix) {
var containerStr = prefix.slice(0, prefix.length - 1);
return prefix ?
Lib.nestedProperty(cont, containerStr).get() || {} :
cont;
Lib.nestedProperty(outerCont, containerStr).get() || {} :
outerCont;
}

module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, opts) {
module.exports = function colorScaleDefaults(outerContIn, outerContOut, layout, coerce, opts) {
var prefix = opts.prefix;
var cLetter = opts.cLetter;
var containerIn = npMaybe(traceIn, prefix);
var containerOut = npMaybe(traceOut, prefix);
var template = npMaybe(traceOut._template || {}, prefix) || {};
var inTrace = '_module' in outerContOut;
var containerIn = npMaybe(outerContIn, prefix);
var containerOut = npMaybe(outerContOut, prefix);
var template = npMaybe(outerContOut._template || {}, prefix) || {};

// colorScaleDefaults wrapper called if-ever we need to reset the colorscale
// attributes for containers that were linked to invalid color axes
var thisFn = function() {
delete outerContIn.coloraxis;
delete outerContOut.coloraxis;
return colorScaleDefaults(outerContIn, outerContOut, layout, coerce, opts);
};

if(inTrace) {
var colorAxes = layout._colorAxes || {};
var colorAx = coerce(prefix + 'coloraxis');

if(colorAx) {
var colorbarVisuals = (
traceIs(outerContOut, 'contour') &&
Lib.nestedProperty(outerContOut, 'contours.coloring').get()
) || 'heatmap';

var stash = colorAxes[colorAx];

if(stash) {
stash[2].push(thisFn);

if(stash[0] !== colorbarVisuals) {
stash[0] = false;
Lib.warn([
'Ignoring coloraxis:', colorAx, 'setting',
'as it is linked to incompatible colorscales.'
].join(' '));
}
} else {
// stash:
// - colorbar visual 'type'
// - colorbar options to help in Colorbar.draw
// - list of colorScaleDefaults wrapper functions
colorAxes[colorAx] = [colorbarVisuals, outerContOut, [thisFn]];
}
return;
}
}

var minIn = containerIn[cLetter + 'min'];
var maxIn = containerIn[cLetter + 'max'];
Expand Down Expand Up @@ -58,7 +101,7 @@ module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce,
// handles both the trace case where the dflt is listed in attributes and
// the marker case where the dflt is determined by hasColorbar
var showScaleDflt;
if(prefix) showScaleDflt = hasColorbar(containerIn);
if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn);

var showScale = coerce(prefix + 'showscale', showScaleDflt);
if(showScale) colorbarDefaults(containerIn, containerOut, layout);
Expand Down
73 changes: 48 additions & 25 deletions src/components/colorscale/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,63 @@

'use strict';

var extendFlat = require('../../lib/extend').extendFlat;

var colorScaleAttrs = require('./attributes');
var scales = require('./scales').scales;

var msg = 'Note that `autocolorscale` must be true for this attribute to work.';

module.exports = {
editType: 'calc',
sequential: {
valType: 'colorscale',
dflt: scales.Reds,
role: 'style',
editType: 'calc',
description: [
'Sets the default sequential colorscale for positive values.',
msg
].join(' ')
},
sequentialminus: {
valType: 'colorscale',
dflt: scales.Blues,
role: 'style',

colorscale: {
editType: 'calc',
description: [
'Sets the default sequential colorscale for negative values.',
msg
].join(' ')

sequential: {
valType: 'colorscale',
dflt: scales.Reds,
role: 'style',
editType: 'calc',
description: [
'Sets the default sequential colorscale for positive values.',
msg
].join(' ')
},
sequentialminus: {
valType: 'colorscale',
dflt: scales.Blues,
role: 'style',
editType: 'calc',
description: [
'Sets the default sequential colorscale for negative values.',
msg
].join(' ')
},
diverging: {
valType: 'colorscale',
dflt: scales.RdBu,
role: 'style',
editType: 'calc',
description: [
'Sets the default diverging colorscale.',
msg
].join(' ')
}
},
diverging: {
valType: 'colorscale',
dflt: scales.RdBu,
role: 'style',

coloraxis: extendFlat({
// not really a 'subplot' attribute container,
// but this is the flag we use to denote attributes that
// support yaxis, yaxis2, yaxis3, ... counters
_isSubplotObj: true,
editType: 'calc',
description: [
'Sets the default diverging colorscale.',
msg
''
].join(' ')
}
}, colorScaleAttrs('', {
colorAttr: 'corresponding trace color array(s)',
noColorAxis: true,
showScaleDflt: true
}))
};
38 changes: 31 additions & 7 deletions src/components/colorscale/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,41 @@
'use strict';

var Lib = require('../../lib');
var colorscaleAttrs = require('./layout_attributes');
var Template = require('../../plot_api/plot_template');

var colorScaleAttrs = require('./layout_attributes');
var colorScaleDefaults = require('./defaults');

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var colorscaleIn = layoutIn.colorscale;
var colorscaleOut = Template.newContainer(layoutOut, 'colorscale');
function coerce(attr, dflt) {
return Lib.coerce(colorscaleIn, colorscaleOut, colorscaleAttrs, attr, dflt);
return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt);
}

coerce('sequential');
coerce('sequentialminus');
coerce('diverging');
coerce('colorscale.sequential');
coerce('colorscale.sequentialminus');
coerce('colorscale.diverging');

var colorAxes = layoutOut._colorAxes;
var colorAxIn, colorAxOut;

function coerceAx(attr, dflt) {
return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt);
}

for(var k in colorAxes) {
var stash = colorAxes[k];

if(stash[0]) {
colorAxIn = layoutIn[k] || {};
colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis');
colorAxOut._name = k;
colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'});
} else {
// re-coerce colorscale attributes w/o coloraxis
for(var i = 0; i < stash[2].length; i++) {
stash[2][i]();
}
delete layoutOut._colorAxes[k];
}
}
};
30 changes: 16 additions & 14 deletions src/plot_api/plot_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,24 +544,26 @@ function getLayoutAttributes() {
var schema = _module.schema;

if(schema && (schema.subplots || schema.layout)) {
/*
* Components with defined schema have already been merged in at register time
* but a few components define attributes that apply only to xaxis
* not yaxis (rangeselector, rangeslider) - delete from y schema.
* Note that the input attributes for xaxis/yaxis are the same object
* so it's not possible to only add them to xaxis from the start.
* If we ever have such asymmetry the other way, or anywhere else,
* we will need to extend both this code and mergeComponentAttrsToSubplot
* (which will not find yaxis only for example)
*/

/*
* Components with defined schema have already been merged in at register time
* but a few components define attributes that apply only to xaxis
* not yaxis (rangeselector, rangeslider) - delete from y schema.
* Note that the input attributes for xaxis/yaxis are the same object
* so it's not possible to only add them to xaxis from the start.
* If we ever have such asymmetry the other way, or anywhere else,
* we will need to extend both this code and mergeComponentAttrsToSubplot
* (which will not find yaxis only for example)
*/
var subplots = schema.subplots;
if(subplots && subplots.xaxis && !subplots.yaxis) {
for(var xkey in subplots.xaxis) delete layoutAttributes.yaxis[xkey];
for(var xkey in subplots.xaxis) {
delete layoutAttributes.yaxis[xkey];
}
}
} else if(_module.name === 'colorscale') {
extendDeepAll(layoutAttributes, _module.layoutAttributes);
} else if(_module.layoutAttributes) {
// older style without schema need to be explicitly merged in now

// older style without schema need to be explicitly merged in now
insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/plots/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
var fontAttrs = require('./font_attributes');
var animationAttrs = require('./animation_attributes');
var colorAttrs = require('../components/color/attributes');
var colorscaleAttrs = require('../components/colorscale/layout_attributes');
var padAttrs = require('./pad_attributes');
var extendFlat = require('../lib/extend').extendFlat;

Expand Down Expand Up @@ -292,7 +291,6 @@ module.exports = {
editType: 'calc',
description: 'Sets the default trace colors.'
},
colorscale: colorscaleAttrs,
datarevision: {
valType: 'any',
role: 'info',
Expand Down
2 changes: 2 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ plots.supplyDefaults = function(gd, opts) {
newFullLayout._firstScatter = {};
// for grouped bar/box/violin trace to share config across traces
newFullLayout._alignmentOpts = {};
// track color axes referenced in the data
newFullLayout._colorAxes = {};

// for traces to request a default rangeslider on their x axes
// eg set `_requestRangeslider.x2 = true` for xaxis2
Expand Down
5 changes: 3 additions & 2 deletions test/jasmine/bundle_tests/plotschema_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ describe('plot schema', function() {
'xaxis', 'yaxis', 'scene', 'geo', 'ternary', 'mapbox', 'polar',
// not really a 'subplot' object but supports yaxis, yaxis2, yaxis3,
// ... counters, so list it here
'xaxis.rangeslider.yaxis'
'xaxis.rangeslider.yaxis',
'coloraxis'
];

// check if the subplot objects have '_isSubplotObj'
Expand All @@ -146,7 +147,7 @@ describe('plot schema', function() {
plotSchema.layout.layoutAttributes,
astr + '.' + IS_SUBPLOT_OBJ
).get()
).toBe(true);
).toBe(true, astr);
});

// check that no other object has '_isSubplotObj'
Expand Down
Loading

0 comments on commit ae3f407

Please sign in to comment.