Skip to content

Commit be38e93

Browse files
committed
stacked area charts!
1 parent 68b489d commit be38e93

24 files changed

+997
-56
lines changed

src/components/errorbars/calc.js

+20-3
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,29 @@ function calcOneAxis(calcTrace, trace, axis, coord) {
4343
var computeError = makeComputeError(opts);
4444

4545
for(var i = 0; i < calcTrace.length; i++) {
46-
var calcPt = calcTrace[i],
47-
calcCoord = calcPt[coord];
46+
var calcPt = calcTrace[i];
47+
48+
var iIn = calcPt.i;
49+
50+
// for types that don't include `i` in each calcdata point
51+
if(iIn === undefined) iIn = i;
52+
53+
// for stacked area inserted points
54+
// TODO: errorbars have been tested cursorily with stacked area,
55+
// but not thoroughly. It's not even really clear what you want to do:
56+
// Should it just be calculated based on that trace's size data?
57+
// Should you add errors from below in quadrature?
58+
// And what about normalization, where in principle the errors shrink
59+
// again when you get up to the top end?
60+
// One option would be to forbid errorbars with stacking until we
61+
// decide how to handle these questions.
62+
else if(iIn === null) continue;
63+
64+
var calcCoord = calcPt[coord];
4865

4966
if(!isNumeric(axis.c2l(calcCoord))) continue;
5067

51-
var errors = computeError(calcCoord, i);
68+
var errors = computeError(calcCoord, iIn);
5269
if(isNumeric(errors[0]) && isNumeric(errors[1])) {
5370
var shoe = calcPt[coord + 's'] = calcCoord - errors[0],
5471
hat = calcPt[coord + 'h'] = calcCoord + errors[1];

src/plots/plots.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,11 @@ plots.supplyDefaults = function(gd, opts) {
391391
// initialize splom grid defaults
392392
newFullLayout._splomGridDflt = {};
393393

394+
// for stacked area traces to share config across traces
395+
newFullLayout._scatterStackOpts = {};
396+
// for the first scatter trace on each subplot (so it knows tonext->tozero)
397+
newFullLayout._firstScatter = {};
398+
394399
// for traces to request a default rangeslider on their x axes
395400
// eg set `_requestRangeslider.x2 = true` for xaxis2
396401
newFullLayout._requestRangeslider = {};
@@ -938,8 +943,6 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
938943
fullTrace.uid = fullLayout._traceUids[i];
939944
plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);
940945

941-
fullTrace.uid = fullLayout._traceUids[i];
942-
943946
fullTrace.index = i;
944947
fullTrace._input = trace;
945948
fullTrace._expandedIndex = cnt;
@@ -1178,6 +1181,14 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
11781181

11791182
plots.supplyTransformDefaults(traceIn, traceOut, layout);
11801183
}
1184+
else if(_module && Registry.traceIs(traceOut, 'alwaysSupplyDefaults')) {
1185+
// Some types need *something* from supplyDefaults always, even if
1186+
// visible: false. Looking at you scatter: stack options even from
1187+
// hidden traces can control other traces in the stack.
1188+
// These types should bail out ASAP if visible is false.
1189+
// But we don't need any other cross-module attrs ^^ in this case.
1190+
_module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
1191+
}
11811192

11821193
return traceOut;
11831194
};
@@ -1556,7 +1567,6 @@ plots.purge = function(gd) {
15561567
// (and to have a record of them...)
15571568
delete gd._promises;
15581569
delete gd._redrawTimer;
1559-
delete gd.firstscatter;
15601570
delete gd._hmlumcount;
15611571
delete gd._hmpixcount;
15621572
delete gd._transitionData;
@@ -2421,8 +2431,6 @@ plots.doCalcdata = function(gd, traces) {
24212431
gd.calcdata = calcdata;
24222432

24232433
// extra helper variables
2424-
// firstscatter: fill-to-next on the first trace goes to zero
2425-
gd.firstscatter = true;
24262434

24272435
// how many box/violins plots do we have (in case they're grouped)
24282436
fullLayout._numBoxes = 0;

src/traces/bar/layout_attributes.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ module.exports = {
3636
editType: 'calc',
3737
description: [
3838
'Sets the normalization for bar traces on the graph.',
39-
'With *fraction*, the value of each bar is divide by the sum of the',
40-
'values at the location coordinate.',
41-
'With *percent*, the results form *fraction* are presented in percents.'
39+
'With *fraction*, the value of each bar is divided by the sum of all',
40+
'values at that location coordinate.',
41+
'*percent* is the same but multiplied by 100 to show percentages.'
4242
].join(' ')
4343
},
4444
bargap: {

src/traces/scatter/attributes.js

+67-2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,69 @@ module.exports = {
7272
'See `y0` for more info.'
7373
].join(' ')
7474
},
75+
76+
stackgroup: {
77+
valType: 'string',
78+
role: 'info',
79+
dflt: '',
80+
editType: 'calc',
81+
description: [
82+
'Set several scatter traces (on the same subplot) to the same',
83+
'stackgroup in order to add their y values (or their x values if',
84+
'`orientation` is *h*). If blank or omitted this trace will not be',
85+
'stacked. Stacking also turns `fill` on by default, using *tonexty*',
86+
'(*tonextx*) if `orientation` is *h* (*v*) and sets the default',
87+
'`mode` to *lines* irrespective of point count.',
88+
'You can only stack on a numeric (linear or log) axis.'
89+
].join(' ')
90+
},
91+
orientation: {
92+
valType: 'enumerated',
93+
role: 'info',
94+
values: ['v', 'h'],
95+
editType: 'calc',
96+
description: [
97+
'Only relevant when `stackgroup` is used, and only the first',
98+
'`orientation` found in the `stackgroup` will be used. Sets the',
99+
'stacking direction. With *v* (*h*), the y (x) values of subsequent',
100+
'traces are added. Also affects the default value of `fill`.'
101+
].join(' ')
102+
},
103+
groupnorm: {
104+
valType: 'enumerated',
105+
values: ['', 'fraction', 'percent'],
106+
dflt: '',
107+
role: 'info',
108+
editType: 'calc',
109+
description: [
110+
'Only relevant when `stackgroup` is used, and only the first',
111+
'`groupnorm` found in the `stackgroup` will be used.',
112+
'Sets the normalization for the sum of this `stackgroup`.',
113+
'With *fraction*, the value of each trace at each location is',
114+
'divided by the sum of all trace values at that location.',
115+
'*percent* is the same but multiplied by 100 to show percentages.'
116+
].join(' ')
117+
},
118+
stackgaps: {
119+
valType: 'enumerated',
120+
values: ['infer zero', 'interpolate'],
121+
dflt: 'infer zero',
122+
role: 'info',
123+
editType: 'calc',
124+
description: [
125+
'Only relevant when `stackgroup` is used, and only the first',
126+
'`stackgaps` found in the `stackgroup` will be used.',
127+
'Determines how we handle locations at which other traces in this',
128+
'group have data but this one does not.',
129+
'With *infer zero* we insert a zero at these locations.',
130+
'With *interpolate* we linearly interpolate between existing',
131+
'values, and extrapolate a constant beyond the existing values.'
132+
// TODO - implement interrupt mode
133+
// '*interrupt* omits this trace from the stack at this location by',
134+
// 'dropping abruptly, midway between the existing and missing locations.'
135+
].join(' ')
136+
},
137+
75138
text: {
76139
valType: 'string',
77140
role: 'info',
@@ -114,7 +177,8 @@ module.exports = {
114177
'If the provided `mode` includes *text* then the `text` elements',
115178
'appear at the coordinates. Otherwise, the `text` elements',
116179
'appear on hover.',
117-
'If there are less than ' + constants.PTS_LINESONLY + ' points,',
180+
'If there are less than ' + constants.PTS_LINESONLY + ' points',
181+
'and the trace is not stacked',
118182
'then the default is *lines+markers*. Otherwise, *lines*.'
119183
].join(' ')
120184
},
@@ -212,11 +276,12 @@ module.exports = {
212276
fill: {
213277
valType: 'enumerated',
214278
values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
215-
dflt: 'none',
216279
role: 'style',
217280
editType: 'calc',
218281
description: [
219282
'Sets the area to fill with a solid color.',
283+
'Defaults to *none* unless this trace is stacked, then it gets',
284+
'*tonexty* (*tonextx*) if `orientation` is *v* (*h*)',
220285
'Use with `fillcolor` if not *none*.',
221286
'*tozerox* and *tozeroy* fill to x=0 and y=0 respectively.',
222287
'*tonextx* and *tonexty* fill between the endpoints of this',

0 commit comments

Comments
 (0)