Skip to content

Commit

Permalink
stacked area charts!
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcjohnson committed Sep 1, 2018
1 parent 68b489d commit be38e93
Show file tree
Hide file tree
Showing 24 changed files with 997 additions and 56 deletions.
23 changes: 20 additions & 3 deletions src/components/errorbars/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,29 @@ function calcOneAxis(calcTrace, trace, axis, coord) {
var computeError = makeComputeError(opts);

for(var i = 0; i < calcTrace.length; i++) {
var calcPt = calcTrace[i],
calcCoord = calcPt[coord];
var calcPt = calcTrace[i];

var iIn = calcPt.i;

// for types that don't include `i` in each calcdata point
if(iIn === undefined) iIn = i;

// for stacked area inserted points
// TODO: errorbars have been tested cursorily with stacked area,
// but not thoroughly. It's not even really clear what you want to do:
// Should it just be calculated based on that trace's size data?
// Should you add errors from below in quadrature?
// And what about normalization, where in principle the errors shrink
// again when you get up to the top end?
// One option would be to forbid errorbars with stacking until we
// decide how to handle these questions.
else if(iIn === null) continue;

var calcCoord = calcPt[coord];

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

var errors = computeError(calcCoord, i);
var errors = computeError(calcCoord, iIn);
if(isNumeric(errors[0]) && isNumeric(errors[1])) {
var shoe = calcPt[coord + 's'] = calcCoord - errors[0],
hat = calcPt[coord + 'h'] = calcCoord + errors[1];
Expand Down
18 changes: 13 additions & 5 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,11 @@ plots.supplyDefaults = function(gd, opts) {
// initialize splom grid defaults
newFullLayout._splomGridDflt = {};

// for stacked area traces to share config across traces
newFullLayout._scatterStackOpts = {};
// for the first scatter trace on each subplot (so it knows tonext->tozero)
newFullLayout._firstScatter = {};

// for traces to request a default rangeslider on their x axes
// eg set `_requestRangeslider.x2 = true` for xaxis2
newFullLayout._requestRangeslider = {};
Expand Down Expand Up @@ -938,8 +943,6 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
fullTrace.uid = fullLayout._traceUids[i];
plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);

fullTrace.uid = fullLayout._traceUids[i];

fullTrace.index = i;
fullTrace._input = trace;
fullTrace._expandedIndex = cnt;
Expand Down Expand Up @@ -1178,6 +1181,14 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac

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

return traceOut;
};
Expand Down Expand Up @@ -1556,7 +1567,6 @@ plots.purge = function(gd) {
// (and to have a record of them...)
delete gd._promises;
delete gd._redrawTimer;
delete gd.firstscatter;
delete gd._hmlumcount;
delete gd._hmpixcount;
delete gd._transitionData;
Expand Down Expand Up @@ -2421,8 +2431,6 @@ plots.doCalcdata = function(gd, traces) {
gd.calcdata = calcdata;

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

// how many box/violins plots do we have (in case they're grouped)
fullLayout._numBoxes = 0;
Expand Down
6 changes: 3 additions & 3 deletions src/traces/bar/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ module.exports = {
editType: 'calc',
description: [
'Sets the normalization for bar traces on the graph.',
'With *fraction*, the value of each bar is divide by the sum of the',
'values at the location coordinate.',
'With *percent*, the results form *fraction* are presented in percents.'
'With *fraction*, the value of each bar is divided by the sum of all',
'values at that location coordinate.',
'*percent* is the same but multiplied by 100 to show percentages.'
].join(' ')
},
bargap: {
Expand Down
69 changes: 67 additions & 2 deletions src/traces/scatter/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,69 @@ module.exports = {
'See `y0` for more info.'
].join(' ')
},

stackgroup: {
valType: 'string',
role: 'info',
dflt: '',
editType: 'calc',
description: [
'Set several scatter traces (on the same subplot) to the same',
'stackgroup in order to add their y values (or their x values if',
'`orientation` is *h*). If blank or omitted this trace will not be',
'stacked. Stacking also turns `fill` on by default, using *tonexty*',
'(*tonextx*) if `orientation` is *h* (*v*) and sets the default',
'`mode` to *lines* irrespective of point count.',
'You can only stack on a numeric (linear or log) axis.'
].join(' ')
},
orientation: {
valType: 'enumerated',
role: 'info',
values: ['v', 'h'],
editType: 'calc',
description: [
'Only relevant when `stackgroup` is used, and only the first',
'`orientation` found in the `stackgroup` will be used. Sets the',
'stacking direction. With *v* (*h*), the y (x) values of subsequent',
'traces are added. Also affects the default value of `fill`.'
].join(' ')
},
groupnorm: {
valType: 'enumerated',
values: ['', 'fraction', 'percent'],
dflt: '',
role: 'info',
editType: 'calc',
description: [
'Only relevant when `stackgroup` is used, and only the first',
'`groupnorm` found in the `stackgroup` will be used.',
'Sets the normalization for the sum of this `stackgroup`.',
'With *fraction*, the value of each trace at each location is',
'divided by the sum of all trace values at that location.',
'*percent* is the same but multiplied by 100 to show percentages.'
].join(' ')
},
stackgaps: {
valType: 'enumerated',
values: ['infer zero', 'interpolate'],
dflt: 'infer zero',
role: 'info',
editType: 'calc',
description: [
'Only relevant when `stackgroup` is used, and only the first',
'`stackgaps` found in the `stackgroup` will be used.',
'Determines how we handle locations at which other traces in this',
'group have data but this one does not.',
'With *infer zero* we insert a zero at these locations.',
'With *interpolate* we linearly interpolate between existing',
'values, and extrapolate a constant beyond the existing values.'
// TODO - implement interrupt mode
// '*interrupt* omits this trace from the stack at this location by',
// 'dropping abruptly, midway between the existing and missing locations.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down Expand Up @@ -114,7 +177,8 @@ module.exports = {
'If the provided `mode` includes *text* then the `text` elements',
'appear at the coordinates. Otherwise, the `text` elements',
'appear on hover.',
'If there are less than ' + constants.PTS_LINESONLY + ' points,',
'If there are less than ' + constants.PTS_LINESONLY + ' points',
'and the trace is not stacked',
'then the default is *lines+markers*. Otherwise, *lines*.'
].join(' ')
},
Expand Down Expand Up @@ -212,11 +276,12 @@ module.exports = {
fill: {
valType: 'enumerated',
values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
dflt: 'none',
role: 'style',
editType: 'calc',
description: [
'Sets the area to fill with a solid color.',
'Defaults to *none* unless this trace is stacked, then it gets',
'*tonexty* (*tonextx*) if `orientation` is *v* (*h*)',
'Use with `fillcolor` if not *none*.',
'*tozerox* and *tozeroy* fill to x=0 and y=0 respectively.',
'*tonextx* and *tonexty* fill between the endpoints of this',
Expand Down
Loading

0 comments on commit be38e93

Please sign in to comment.