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

Waterfall charts #3531

Merged
merged 78 commits into from
Mar 28, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8f89564
started waterfall
archmoj Feb 4, 2019
5678653
rm traceIs waterfall as it described as bar
archmoj Feb 11, 2019
cfe8eb2
isFall > isSum as we may use rise and fall for positive and negative …
archmoj Feb 11, 2019
dbc98b5
separated calc for waterfall - pass 1
archmoj Feb 11, 2019
acc3640
removed barnorm stack and base
archmoj Feb 11, 2019
9537ee8
removed more useless mocks
archmoj Feb 11, 2019
6be0a4a
easier to use api
archmoj Feb 11, 2019
d6a69f2
new baselines
archmoj Feb 12, 2019
34b6a0a
handle undefined cases in extract instructions
archmoj Feb 12, 2019
aa58571
new baselines
archmoj Feb 12, 2019
6a4666a
auto compute color array based on vals when array left empty
archmoj Feb 12, 2019
3fb3597
new baselines
archmoj Feb 12, 2019
dc1b420
defualt to remove instructions from ax text
archmoj Feb 12, 2019
f8507ce
only accept valid tokens
archmoj Feb 12, 2019
a875ac7
new baselines
archmoj Feb 12, 2019
69956a0
only run waterfalls on CI for now
archmoj Feb 12, 2019
82dfd18
use operators instead of instr and token
archmoj Feb 12, 2019
87ad17c
handle initial values
archmoj Feb 12, 2019
b0fdced
correct baselines cases without an initial value
archmoj Feb 12, 2019
4c4c8e5
refactor plot using isHorizontal and else fixup
archmoj Feb 13, 2019
c052205
describe connectors
archmoj Feb 13, 2019
fb28e30
draw connector at the end of each bar
archmoj Feb 13, 2019
ea4863f
extend connectors using dtick
archmoj Feb 13, 2019
af60c1f
fixed shape and dist between connectors
archmoj Feb 13, 2019
1827ee6
new baselines
archmoj Feb 13, 2019
89e66bb
revised includes
archmoj Feb 13, 2019
4383a71
providing a separate array for the operators
archmoj Feb 13, 2019
2454e6c
removed last connector
archmoj Feb 13, 2019
3533448
new baselines without last connector
archmoj Feb 13, 2019
cd16944
Merge branch 'master' into waterfall-new-trace_dev1
archmoj Mar 1, 2019
f9b36cb
consider alignment and offset group across matching axes in waterfall
archmoj Mar 1, 2019
3386803
removed few unused parameters from bar cross_trace_calc
archmoj Mar 5, 2019
d1c8c48
inherit from bars - pass 1
archmoj Mar 6, 2019
cd81dcd
rm triangles
archmoj Mar 6, 2019
e7602e2
removed operator - used null magic number and added initialized attri…
archmoj Mar 6, 2019
d1bbae6
removed errorbar support
archmoj Mar 6, 2019
59ee151
removed errorbars
archmoj Mar 8, 2019
d8d54cd
support non-array base - waterfall tests
archmoj Mar 13, 2019
c1ed50f
revised connector line shapes
archmoj Mar 14, 2019
346c637
revised connectors - hover showing initial - multicategory example
archmoj Mar 14, 2019
fed3c97
Revision based on Etienne review - pass 1
archmoj Mar 18, 2019
aa8abe4
fixup attributes width offset connector lines plot and test
archmoj Mar 20, 2019
a05352f
arrayOk width and offset back - apply offset arrays in connector draw
archmoj Mar 20, 2019
20d1cbc
added totals container and fixed hover colors
archmoj Mar 21, 2019
692c6d4
fix hover color in test
archmoj Mar 21, 2019
c0b1017
waterfallmode
archmoj Mar 26, 2019
9533558
waterfallgap
archmoj Mar 26, 2019
2480892
waterfallgroupgap
archmoj Mar 26, 2019
f3f7d84
fixup waterfall mode gap groupgap
archmoj Mar 26, 2019
7e41079
rename inner node in <g.lines> -> <g.line>
etpinard Mar 26, 2019
84e3a27
fix `cliponaxis:false` for waterfall traces
etpinard Mar 26, 2019
00070ba
always drawn waterfall trace below bar/histogram
etpinard Mar 26, 2019
dcab107
fixup waterfall/bar mode/gap/groupgap interplay
etpinard Mar 26, 2019
d52116b
revised waterfall marker logic
archmoj Mar 27, 2019
6d37db9
drop steps connector mode
archmoj Mar 27, 2019
fb0d7a9
added connector visible drop false mode
archmoj Mar 27, 2019
9e941ba
tweak bar endpoints to adjust with connector in mode between
archmoj Mar 27, 2019
2a25753
dont coerce connector if visible false
archmoj Mar 27, 2019
1825027
turn connector.(color|dash|width) into -> connector.line.(color|dash|…
archmoj Mar 27, 2019
4a044e4
make restyle connector visible (true|false) work
archmoj Mar 27, 2019
c53bcce
fixup restyle connector visible remove nodes
archmoj Mar 27, 2019
3b984ff
added connector restyle visible true false
archmoj Mar 27, 2019
6b3eeab
no need to loop over all the traces when waterfallmode is not group
archmoj Mar 27, 2019
ce0a56a
make waterfall NOT a 'bar' category
etpinard Mar 27, 2019
412bf9d
revamp waterfall marker style logic
etpinard Mar 27, 2019
a55777f
:hocho: [un]selected style container for waterfall (for now)
etpinard Mar 27, 2019
dee39dc
do not show hover label when size===0
etpinard Mar 27, 2019
a138ebd
adapt waterfall legend to new *marker* styles ...
etpinard Mar 27, 2019
7019a12
fixup (from :hocho: 'bar' category commit)
etpinard Mar 27, 2019
8bbc863
fixup waterfall mocks and baselines
etpinard Mar 27, 2019
b6fdaed
fixup mock list
etpinard Mar 27, 2019
cb8f1ec
adapt failing waterfall jasmine tests
archmoj Mar 27, 2019
1447b40
fixup waterfall bar path query
etpinard Mar 27, 2019
4eb5daf
add a flaky flag for one test
archmoj Mar 28, 2019
f3a071e
waterfall marker description - set default to between mode - added sp…
archmoj Mar 28, 2019
1721ab7
fix waterfall connectors - not to draw lines between null amounts - u…
archmoj Mar 28, 2019
7655664
Merge remote-tracking branch 'origin/master' into waterfall-new-trace
archmoj Mar 28, 2019
1d275f5
set a noCI flag for the select test
archmoj Mar 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Plotly.register([
require('./contour'),
require('./scatterternary'),
require('./violin'),
require('./waterfall'),

require('./scatter3d'),
require('./surface'),
Expand Down
11 changes: 11 additions & 0 deletions lib/waterfall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

module.exports = require('../src/traces/waterfall');
5 changes: 4 additions & 1 deletion src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
var trace = d[0].trace;
var xcalendar = trace.xcalendar;
var ycalendar = trace.ycalendar;
var selector = trace.type === 'bar' ? '.bartext' : '.point,.textpoint';
var selector = (
trace.type === 'bar' ||
trace.type === 'waterfall') ?
'.bartext' : '.point,.textpoint';

traceGroups.selectAll(selector).each(function(d) {
drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
Expand Down
4 changes: 3 additions & 1 deletion src/components/legend/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
}
}

if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') ||
if(((Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')) &&
layoutOut.barmode === 'stack') ||
['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) {
defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ?
'grouped+reversed' : 'reversed';
Expand Down
5 changes: 4 additions & 1 deletion src/components/legend/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ module.exports = function style(s, gd) {

var barpath = d3.select(this).select('g.legendpoints')
.selectAll('path.legendbar')
.data(Registry.traceIs(trace, 'bar') ? [d] : []);
.data((
Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')
) ? [d] : []);
barpath.enter().append('path').classed('legendbar', true)
.attr('d', 'M6,6H-6V-6H6Z')
.attr('transform', 'translate(20,0)');
Expand Down
14 changes: 10 additions & 4 deletions src/plot_api/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ exports.cleanData = function(data) {
// error_y.opacity is obsolete - merge into color
if(trace.error_y && 'opacity' in trace.error_y) {
var dc = Color.defaults;
var yeColor = trace.error_y.color || (Registry.traceIs(trace, 'bar') ?
var yeColor = trace.error_y.color ||
((Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')) ?
Color.defaultLine :
dc[tracei % dc.length]);
trace.error_y.color = Color.addOpacity(
Expand All @@ -302,8 +304,10 @@ exports.cleanData = function(data) {
// convert bardir to orientation, and put the data into
// the axes it's eventually going to be used with
if('bardir' in trace) {
if(trace.bardir === 'h' && (Registry.traceIs(trace, 'bar') ||
trace.type.substr(0, 9) === 'histogram')) {
if(trace.bardir === 'h' &&
((Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')) ||
trace.type.substr(0, 9) === 'histogram')) {
trace.orientation = 'h';
exports.swapXYData(trace);
}
Expand Down Expand Up @@ -336,7 +340,9 @@ exports.cleanData = function(data) {
trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
}

if(!Registry.traceIs(trace, 'pie') && !Registry.traceIs(trace, 'bar')) {
if(!Registry.traceIs(trace, 'pie') &&
!Registry.traceIs(trace, 'bar') &&
!Registry.traceIs(trace, 'waterfall')) {
if(Array.isArray(trace.textposition)) {
for(i = 0; i < trace.textposition.length; i++) {
trace.textposition[i] = cleanTextPosition(trace.textposition[i]);
Expand Down
6 changes: 5 additions & 1 deletion src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1670,7 +1670,11 @@ function _restyle(gd, aobj, traces) {
else if(ai === 'type' && (newVal === 'pie') !== (oldVal === 'pie')) {
var labelsTo = 'x';
var valuesTo = 'y';
if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
if(cont.orientation === 'h' && (
newVal === 'bar' ||
oldVal === 'bar' ||
newVal === 'waterfall' ||
oldVal === 'waterfall')) {
labelsTo = 'y';
valuesTo = 'x';
}
Expand Down
4 changes: 3 additions & 1 deletion src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2822,7 +2822,9 @@ function hasBarsOrFill(gd, ax) {
if(trace.visible === true &&
(trace.xaxis + trace.yaxis) === subplot &&
(
Registry.traceIs(trace, 'bar') && trace.orientation === {x: 'h', y: 'v'}[axLetter] ||
(Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')) &&
trace.orientation === {x: 'h', y: 'v'}[axLetter] ||
trace.fill && trace.fill.charAt(trace.fill.length - 1) === axLetter
)
) {
Expand Down
26 changes: 22 additions & 4 deletions src/traces/bar/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ var calcSelection = require('../scatter/calc_selection');
module.exports = function calc(gd, trace) {
etpinard marked this conversation as resolved.
Show resolved Hide resolved
var xa = Axes.getFromId(gd, trace.xaxis || 'x');
var ya = Axes.getFromId(gd, trace.yaxis || 'y');
var size, pos;
var size, pos, i;

var isWaterfall = (trace.type === 'waterfall');

if(trace.orientation === 'h') {
size = xa.makeCalcdata(trace, 'x');
Expand All @@ -31,9 +33,25 @@ module.exports = function calc(gd, trace) {
var serieslen = Math.min(pos.length, size.length);
var cd = new Array(serieslen);

// set position and size
for(var i = 0; i < serieslen; i++) {
cd[i] = { p: pos[i], s: size[i] };
// set position and size (as well as for waterfall total size)
var previousSum = 0;
for(i = 0; i < serieslen; i++) {
cd[i] = {
p: pos[i],
s: size[i]
};

if(isWaterfall) {
if(cd[i].s === undefined) {
etpinard marked this conversation as resolved.
Show resolved Hide resolved
cd[i].isFall = true;
cd[i].s = previousSum;
} else {
cd[i].isFall = false;
var newSize = cd[i].s;
cd[i].s += previousSum;
previousSum += newSize;
}
}

if(trace.ids) {
cd[i].id = String(trace.ids[i]);
Expand Down
11 changes: 10 additions & 1 deletion src/traces/bar/cross_trace_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ function crossTraceCalc(gd, plotinfo) {
var fullTrace = fullTraces[i];
if(
fullTrace.visible === true &&
Registry.traceIs(fullTrace, 'bar') &&
(Registry.traceIs(fullTrace, 'bar') ||
Registry.traceIs(fullTrace, 'waterfall')) &&
etpinard marked this conversation as resolved.
Show resolved Hide resolved
fullTrace.xaxis === xa._id &&
fullTrace.yaxis === ya._id
) {
Expand Down Expand Up @@ -646,6 +647,8 @@ function collectExtents(calcTraces, pa) {
cd = calcTraces[i];
cd[0].t.extents = extents;

var isWaterfall = (cd[0].trace.type === 'waterfall');

var poffset = cd[0].t.poffset;
var poffsetIsArray = Array.isArray(poffset);

Expand All @@ -667,6 +670,12 @@ function collectExtents(calcTraces, pa) {
di.p1 = di.p0 + di.w;
di.s0 = di.b;
di.s1 = di.s0 + di.s;

if(isWaterfall) {
if(di.isFall === false) {
di.s0 += (j === 0) ? 0 : cd[j - 1].s;
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/traces/bar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var Color = require('../../components/color');
var Registry = require('../../registry');

var handleXYDefaults = require('../scatter/xy_defaults');
var handleStyleDefaults = require('../bar/style_defaults');
var handleStyleDefaults = require('./style_defaults');
var attributes = require('./attributes');

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
Expand Down
3 changes: 3 additions & 0 deletions src/traces/bar/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ function hoverPoints(pointData, xval, yval, hovermode) {

var size = (trace.base) ? di.b + di.s : di.s;
pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true);
if(trace.type === 'waterfall' && di.isFall === false) {
size -= (index === 0) ? 0 : cd[index - 1].s;
}
pointData[sizeLetter + 'LabelVal'] = size;

var extent = t.extents[t.extents.round(di.p)];
Expand Down
4 changes: 3 additions & 1 deletion src/traces/bar/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ module.exports = function(layoutIn, layoutOut, fullData) {

for(var i = 0; i < fullData.length; i++) {
var trace = fullData[i];
if(Registry.traceIs(trace, 'bar') && trace.visible) hasBars = true;
if((Registry.traceIs(trace, 'bar') ||
Registry.traceIs(trace, 'waterfall')) &&
trace.visible) hasBars = true;
else continue;

// if we have at least 2 grouped bar traces on the same subplot,
Expand Down
23 changes: 19 additions & 4 deletions src/traces/bar/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
var cd0 = cd[0];
var trace = cd0.trace;

var isWaterfall = (trace.type === 'waterfall');
var isTriangle = (isWaterfall) ? (trace.marker.shape === 'triangle') : false;
var isVertical = (trace.orientation === 'v');

if(!plotinfo.isRangePlot) cd0.node3 = plotGroup;

var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
Expand Down Expand Up @@ -111,6 +115,7 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
// pixelation. if the bars ARE fully opaque and have
// no line, expand to a full pixel to make sure we
// can see them

var op = Color.opacity(di.mc || trace.marker.color);
var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible;
x0 = fixpx(x0, x1);
Expand All @@ -119,11 +124,21 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
y1 = fixpx(y1, y0);
}

var shape;
if(isWaterfall && isTriangle && i > 0 && cd[i].isFall === false) {
if(isVertical) {
shape = 'M' + x0 + ',' + y0 + 'L' + (0.5 * (x1 + x0)) + ',' + y1 + 'L' + x1 + ',' + y0 + 'Z';
} else {
shape = 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + (0.5 * (y1 + y0)) + 'L' + x0 + ',' + y1 + 'Z';
}
} else {
shape = 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z';
}

Lib.ensureSingle(bar, 'path')
.style('vector-effect', 'non-scaling-stroke')
.attr('d',
'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);
.style('vector-effect', 'non-scaling-stroke')
.attr('d', shape)
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);

appendBarText(gd, bar, cd, i, x0, x1, y0, y1);

Expand Down
68 changes: 68 additions & 0 deletions src/traces/waterfall/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var extendFlat = require('../../lib/extend').extendFlat;

var barAttrs = require('../bar/attributes');

module.exports = {

x: barAttrs.x,
x0: barAttrs.x0,
dx: barAttrs.dx,
y: barAttrs.y,
y0: barAttrs.y0,
dy: barAttrs.dy,

r: barAttrs.r,
t: barAttrs.t,

text: barAttrs.text,
etpinard marked this conversation as resolved.
Show resolved Hide resolved
hovertext: barAttrs.hovertext,
hovertemplate: barAttrs.hovertemplate,

textposition: barAttrs.textposition,

textfont: barAttrs.textfont,

insidetextfont: barAttrs.insidetextfont,

outsidetextfont: barAttrs.outsidetextfont,

constraintext: barAttrs.constraintext,

cliponaxis: barAttrs.cliponaxis,

orientation: barAttrs.orientation,

base: barAttrs.base,

offset: barAttrs.offset,

width: barAttrs.width,

etpinard marked this conversation as resolved.
Show resolved Hide resolved
marker: extendFlat({}, barAttrs.marker, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have two marker containers for waterfall traces: one for increasing bars and one for decreasing bars, similar to how ohlc and candlestick do it.

increasing: directionAttrs(INCREASING_COLOR),
decreasing: directionAttrs(DECREASING_COLOR),

shape: {
valType: 'enumerated',
values: ['rectangle', 'triangle'],
etpinard marked this conversation as resolved.
Show resolved Hide resolved
dflt: 'rectangle',
role: 'style',
editType: 'style',
description: [
'Defines the shape of positive/negative bars on the plot.',
'Namely \'triangle`\ option could be used to emphasize on',
'the direction of the changes.'
].join(' ')
}
}),

selected: barAttrs.selected,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These attribute containers don't appear to be implemented. No big deal, but please remove them from the attribute declaration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we should keep [un]selected.marker.opacity

unselected: barAttrs.unselected
};
Loading