Skip to content

Commit deeabdd

Browse files
committed
introduce new Histogram.crossTraceDefaults
- design to work for histogram AND histogram2d* traces! - design to work with bingroup! - fills in fullLayout._histogramBinOpts for all histogram* traces - replace `binDir` field in histogramBinOpts[i] by `dirs` (an array) to handle cases where x and y bins are in same bingroup
1 parent 54c9183 commit deeabdd

File tree

3 files changed

+244
-2
lines changed

3 files changed

+244
-2
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/**
2+
* Copyright 2012-2019, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var Lib = require('../../lib');
12+
var axisIds = require('../../plots/cartesian/axis_ids');
13+
14+
var traceIs = require('../../registry').traceIs;
15+
var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults;
16+
17+
var nestedProperty = Lib.nestedProperty;
18+
19+
var BINATTRS = [
20+
{aStr: {x: 'xbins.start', y: 'ybins.start'}, name: 'start'},
21+
{aStr: {x: 'xbins.end', y: 'ybins.end'}, name: 'end'},
22+
{aStr: {x: 'xbins.size', y: 'ybins.size'}, name: 'size'},
23+
{aStr: {x: 'nbinsx', y: 'nbinsy'}, name: 'nbins'}
24+
];
25+
26+
var BINDIRECTIONS = ['x', 'y'];
27+
28+
// handle bin attrs and relink auto-determined values so fullData is complete
29+
module.exports = function crossTraceDefaults(fullData, fullLayout) {
30+
var allBinOpts = fullLayout._histogramBinOpts = {};
31+
var isOverlay = fullLayout.barmode === 'overlay';
32+
33+
var histTraces = [];
34+
var mustMatchTracesLookup = {};
35+
var otherTracesList = [];
36+
37+
var traceOut, traces, groupName, binDir;
38+
var i, j, k;
39+
40+
function coerce(attr, dflt) {
41+
return Lib.coerce(traceOut._input, traceOut, traceOut._module.attributes, attr, dflt);
42+
}
43+
44+
function orientation2binDir(traceOut) {
45+
return traceOut.orientation === 'v' ? 'x' : 'y';
46+
}
47+
48+
function getAxisType(traceOut, binDir) {
49+
var ax = axisIds.getFromTrace({_fullLayout: fullLayout}, traceOut, binDir);
50+
return ax.type;
51+
}
52+
53+
function fillBinOpts(traceOut, groupName, binDir) {
54+
// N.B. group traces that don't have a bingroup with themselves
55+
var fallbackGroupName = traceOut.uid + '__' + binDir;
56+
if(!groupName) groupName = fallbackGroupName;
57+
58+
var axType = getAxisType(traceOut, binDir);
59+
var binOpts = allBinOpts[groupName];
60+
61+
if(binOpts) {
62+
if(axType === binOpts.axType) {
63+
binOpts.traces.push(traceOut);
64+
binOpts.dirs.push(binDir);
65+
} else {
66+
groupName = fallbackGroupName;
67+
allBinOpts[groupName] = {
68+
traces: [traceOut],
69+
dirs: [binDir],
70+
axType: axType
71+
};
72+
Lib.warn([
73+
'Attempted to group the bins of trace', traceOut.index,
74+
'set on a', 'type:' + axType, 'axis',
75+
'with bins on', 'type:' + binOpts.axType, 'axis.'
76+
].join(' '));
77+
}
78+
} else {
79+
binOpts = allBinOpts[groupName] = {
80+
traces: [traceOut],
81+
dirs: [binDir],
82+
axType: axType
83+
};
84+
}
85+
86+
traceOut['_' + binDir + 'bingroup'] = groupName;
87+
}
88+
89+
for(i = 0; i < fullData.length; i++) {
90+
traceOut = fullData[i];
91+
92+
if(traceIs(traceOut, 'histogram')) {
93+
histTraces.push(traceOut);
94+
95+
// TODO: this shouldn't be relinked as it's only used within calc
96+
// https://github.com/plotly/plotly.js/issues/749
97+
delete traceOut._autoBinFinished;
98+
99+
// N.B. need to coerce *alignmentgroup* before *bingroup*, as traces
100+
// in same alignmentgroup "have to match"
101+
if(!traceIs(traceOut, '2dMap')) {
102+
handleGroupingDefaults(traceOut._input, traceOut, fullLayout, coerce);
103+
}
104+
}
105+
}
106+
107+
// Look for traces that "have to match", that is:
108+
// - 1d histogram traces on the same subplot with same orientation under barmode:stack,
109+
// - 1d histogram traces on the same subplot with same orientation under barmode:group
110+
for(i = 0; i < histTraces.length; i++) {
111+
traceOut = histTraces[i];
112+
113+
if(!isOverlay && !traceIs(traceOut, '2dMap')) {
114+
groupName = (
115+
axisIds.getAxisGroup(fullLayout, traceOut.xaxis) +
116+
axisIds.getAxisGroup(fullLayout, traceOut.yaxis) +
117+
orientation2binDir(traceOut)
118+
);
119+
120+
if(!mustMatchTracesLookup[groupName]) {
121+
mustMatchTracesLookup[groupName] = [];
122+
}
123+
mustMatchTracesLookup[groupName].push(traceOut);
124+
} else {
125+
otherTracesList.push(traceOut);
126+
}
127+
}
128+
129+
// Setup binOpts for traces that have to match,
130+
// if the traces have a valid bingroup, use that
131+
// if not use axis+binDir groupName
132+
for(groupName in mustMatchTracesLookup) {
133+
traces = mustMatchTracesLookup[groupName];
134+
135+
// no need to 'force' anything when a single
136+
// trace is detected as "must match"
137+
if(traces.length === 1) {
138+
otherTracesList.push(traces[0]);
139+
continue;
140+
}
141+
142+
var binGroupFound = false;
143+
for(i = 0; i < traces.length; i++) {
144+
traceOut = traces[i];
145+
binGroupFound = coerce('bingroup');
146+
break;
147+
}
148+
149+
groupName = binGroupFound || groupName;
150+
151+
for(i = 0; i < traces.length; i++) {
152+
traceOut = traces[i];
153+
var bingroupIn = traceOut._input.bingroup;
154+
if(bingroupIn && bingroupIn !== groupName) {
155+
Lib.warn([
156+
'Trace', traceOut.index, 'must match',
157+
'within bingroup', groupName + '.',
158+
'Ignoring its bingroup:', bingroupIn, 'setting.'
159+
].join(' '));
160+
}
161+
traceOut.bingroup = groupName;
162+
163+
// N.B. no need to worry about 2dMap case
164+
// (where both bin direction are set in each trace)
165+
// as 2dMap trace never "have to match"
166+
fillBinOpts(traceOut, groupName, orientation2binDir(traceOut));
167+
}
168+
}
169+
170+
// setup binOpts for traces that can but don't have to match,
171+
// notice that these traces can be matched with traces that have to match
172+
for(i = 0; i < otherTracesList.length; i++) {
173+
traceOut = otherTracesList[i];
174+
175+
var binGroup = coerce('bingroup');
176+
177+
if(traceIs(traceOut, '2dMap')) {
178+
for(k = 0; k < 2; k++) {
179+
binDir = BINDIRECTIONS[k];
180+
var binGroupInDir = coerce(binDir + 'bingroup',
181+
binGroup ? binGroup + '__' + binDir : null
182+
);
183+
fillBinOpts(traceOut, binGroupInDir, binDir);
184+
}
185+
} else {
186+
fillBinOpts(traceOut, binGroup, orientation2binDir(traceOut));
187+
}
188+
}
189+
190+
// coerce bin attrs!
191+
for(groupName in allBinOpts) {
192+
var binOpts = allBinOpts[groupName];
193+
traces = binOpts.traces;
194+
195+
for(j = 0; j < BINATTRS.length; j++) {
196+
var attrSpec = BINATTRS[j];
197+
var attr = attrSpec.name;
198+
var aStr;
199+
var autoVals;
200+
201+
// nbins(x|y) is moot if we have a size. This depends on
202+
// nbins coming after size in binAttrs.
203+
if(attr === 'nbins' && binOpts.sizeFound) continue;
204+
205+
for(i = 0; i < traces.length; i++) {
206+
traceOut = traces[i];
207+
binDir = binOpts.dirs[i];
208+
aStr = attrSpec.aStr[binDir];
209+
210+
if(nestedProperty(traceOut._input, aStr).get() !== undefined) {
211+
binOpts[attr] = coerce(aStr);
212+
binOpts[attr + 'Found'] = true;
213+
break;
214+
}
215+
216+
autoVals = (traceOut._autoBin || {})[binDir] || {};
217+
if(autoVals[attr]) {
218+
// if this is the *first* autoval
219+
nestedProperty(traceOut, aStr).set(autoVals[attr]);
220+
}
221+
}
222+
223+
// start and end we need to coerce anyway, after having collected the
224+
// first of each into binOpts, in case a trace wants to restrict its
225+
// data to a certain range
226+
if(attr === 'start' || attr === 'end') {
227+
for(; i < traces.length; i++) {
228+
traceOut = traces[i];
229+
if(traceOut['_' + binDir + 'bingroup']) {
230+
autoVals = (traceOut._autoBin || {})[binDir] || {};
231+
coerce(aStr, autoVals[attr]);
232+
}
233+
}
234+
}
235+
236+
if(attr === 'nbins' && !binOpts.sizeFound && !binOpts.nbinsFound) {
237+
traceOut = traces[0];
238+
binOpts[attr] = coerce(aStr);
239+
}
240+
}
241+
}
242+
};

src/traces/histogram2d/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212

1313
attributes: require('./attributes'),
1414
supplyDefaults: require('./defaults'),
15-
crossTraceDefaults: require('./cross_trace_defaults'),
15+
crossTraceDefaults: require('../histogram/cross_trace_defaults'),
1616
calc: require('../heatmap/calc'),
1717
plot: require('../heatmap/plot'),
1818
layerName: 'heatmaplayer',

src/traces/histogram2dcontour/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
module.exports = {
1212
attributes: require('./attributes'),
1313
supplyDefaults: require('./defaults'),
14-
crossTraceDefaults: require('../histogram2d/cross_trace_defaults'),
14+
crossTraceDefaults: require('../histogram/cross_trace_defaults'),
1515
calc: require('../contour/calc'),
1616
plot: require('../contour/plot').plot,
1717
layerName: 'contourlayer',

0 commit comments

Comments
 (0)