Skip to content

Commit d0ad698

Browse files
authored
Merge pull request #1830 from plotly/unique-expanded-trace-coloring
Unique colors for expanded traces
2 parents 5849eab + 25c2951 commit d0ad698

File tree

3 files changed

+113
-2
lines changed

3 files changed

+113
-2
lines changed

src/plots/plots.js

+47-2
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,51 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
659659
}
660660
};
661661

662+
// This function clears any trace attributes with valType: color and
663+
// no set dflt filed in the plot schema. This is needed because groupby (which
664+
// is the only transform for which this currently applies) supplies parent
665+
// trace defaults, then expanded trace defaults. The result is that `null`
666+
// colors are default-supplied and inherited as a color instead of a null.
667+
// The result is that expanded trace default colors have no effect, with
668+
// the final result that groups are indistinguishable. This function clears
669+
// those colors so that individual groupby groups get unique colors.
670+
plots.clearExpandedTraceDefaultColors = function(trace) {
671+
var colorAttrs, path, i;
672+
673+
// This uses weird closure state in order to satisfy the linter rule
674+
// that we can't create functions in a loop.
675+
function locateColorAttrs(attr, attrName, attrs, level) {
676+
path[level] = attrName;
677+
path.length = level + 1;
678+
if(attr.valType === 'color' && attr.dflt === undefined) {
679+
colorAttrs.push(path.join('.'));
680+
}
681+
}
682+
683+
path = [];
684+
685+
// Get the cached colorAttrs:
686+
colorAttrs = trace._module._colorAttrs;
687+
688+
// Or else compute and cache the colorAttrs on the module:
689+
if(!colorAttrs) {
690+
trace._module._colorAttrs = colorAttrs = [];
691+
PlotSchema.crawl(
692+
trace._module.attributes,
693+
locateColorAttrs
694+
);
695+
}
696+
697+
for(i = 0; i < colorAttrs.length; i++) {
698+
var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]);
699+
700+
if(!origprop.get()) {
701+
Lib.nestedProperty(trace, colorAttrs[i]).set(null);
702+
}
703+
}
704+
};
705+
706+
662707
plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
663708
var i, fullTrace, trace;
664709
var modules = fullLayout._modules = [],
@@ -694,8 +739,8 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
694739
var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
695740

696741
for(var j = 0; j < expandedTraces.length; j++) {
697-
var expandedTrace = expandedTraces[j],
698-
fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i);
742+
var expandedTrace = expandedTraces[j];
743+
var fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i);
699744

700745
// mutate uid here using parent uid and expanded index
701746
// to promote consistency between update calls

src/transforms/groupby.js

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var Lib = require('../lib');
1212
var PlotSchema = require('../plot_api/plot_schema');
13+
var Plots = require('../plots/plots');
1314

1415
exports.moduleType = 'transform';
1516

@@ -172,6 +173,8 @@ function transformOne(trace, state) {
172173

173174
newTrace.name = groupName;
174175

176+
Plots.clearExpandedTraceDefaultColors(newTrace);
177+
175178
// there's no need to coerce styleLookup[groupName] here
176179
// as another round of supplyDefaults is done on the transformed traces
177180
newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {});

test/jasmine/tests/transform_groupby_test.js

+63
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var Plotly = require('@lib/index');
2+
var Plots = require('@src/plots/plots');
23
var Lib = require('@src/lib');
34

45
var createGraphDiv = require('../assets/create_graph_div');
@@ -236,9 +237,38 @@ describe('groupby', function() {
236237
done();
237238
});
238239
});
240+
});
241+
242+
describe('many-to-many transforms', function() {
243+
it('varies the color for each expanded trace', function() {
244+
var uniqueColors = {};
245+
var dataOut = [];
246+
var dataIn = [{
247+
y: [1, 2, 3],
248+
transforms: [
249+
{type: 'filter', operation: '<', value: 4},
250+
{type: 'groupby', groups: ['a', 'b', 'c']}
251+
]
252+
}, {
253+
y: [4, 5, 6],
254+
transforms: [
255+
{type: 'filter', operation: '<', value: 4},
256+
{type: 'groupby', groups: ['a', 'b', 'b']}
257+
]
258+
}];
239259

260+
Plots.supplyDataDefaults(dataIn, dataOut, {}, {});
261+
262+
for(var i = 0; i < dataOut.length; i++) {
263+
uniqueColors[dataOut[i].marker.color] = true;
264+
}
265+
266+
// Confirm that five total colors exist:
267+
expect(Object.keys(uniqueColors).length).toEqual(5);
268+
});
240269
});
241270

271+
242272
// these tests can be shortened, once the meaning of edge cases gets clarified
243273
describe('symmetry/degeneracy testing of one-to-many transforms on arbitrary arrays where there is no grouping (implicit 1):', function() {
244274
'use strict';
@@ -662,6 +692,39 @@ describe('groupby', function() {
662692
it('passes with no groups', test(mockData0));
663693
it('passes with empty groups', test(mockData1));
664694
it('passes with falsey groups', test(mockData2));
695+
});
696+
697+
describe('expanded trace coloring', function() {
698+
it('assigns unique colors to each group', function() {
699+
var colors = [];
700+
var dataOut = [];
701+
var dataIn = [{
702+
y: [1, 2, 3],
703+
transforms: [
704+
{type: 'filter', operation: '<', value: 4},
705+
{type: 'groupby', groups: ['a', 'b', 'c']}
706+
]
707+
}, {
708+
y: [4, 5, 6],
709+
transforms: [
710+
{type: 'filter', operation: '<', value: 4},
711+
{type: 'groupby', groups: ['a', 'b', 'b']}
712+
]
713+
}];
714+
715+
Plots.supplyDataDefaults(dataIn, dataOut, {}, {});
665716

717+
for(var i = 0; i < dataOut.length; i++) {
718+
colors.push(dataOut[i].marker.color);
719+
}
720+
721+
expect(colors).toEqual([
722+
'#1f77b4',
723+
'#ff7f0e',
724+
'#2ca02c',
725+
'#d62728',
726+
'#9467bd'
727+
]);
728+
});
666729
});
667730
});

0 commit comments

Comments
 (0)