Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stacked Area Charts #2960

Merged
merged 14 commits into from Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/components/errorbars/calc.js
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
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];
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved

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);
etpinard marked this conversation as resolved.
Show resolved Hide resolved
}

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
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.'
etpinard marked this conversation as resolved.
Show resolved Hide resolved
].join(' ')
},
bargap: {
Expand Down
69 changes: 67 additions & 2 deletions src/traces/scatter/attributes.js
Expand Up @@ -72,6 +72,69 @@ module.exports = {
'See `y0` for more info.'
].join(' ')
},

stackgroup: {
etpinard marked this conversation as resolved.
Show resolved Hide resolved
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