Skip to content

Commit 0ad049c

Browse files
authored
Merge pull request #4663 from plotly/rangebreaks-waterfall-connectors
Handle NaN positions in funnel & waterfall and skip connectors over rangebreaks
2 parents d91e017 + d9f0002 commit 0ad049c

File tree

5 files changed

+133
-21
lines changed

5 files changed

+133
-21
lines changed

src/traces/funnel/plot.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var d3 = require('d3');
1212
var Lib = require('../../lib');
1313
var Drawing = require('../../components/drawing');
14+
var BADNUM = require('../../constants/numerical').BADNUM;
1415
var barPlot = require('../bar/plot');
1516
var clearMinTextSize = require('../bar/uniform_text').clearMinTextSize;
1617

@@ -66,14 +67,21 @@ function plotConnectorRegions(gd, plotinfo, cdModule, traceLayer) {
6667

6768
var shape = '';
6869

69-
if(x[3] !== undefined && y[3] !== undefined) {
70+
if(
71+
x[0] !== BADNUM && y[0] !== BADNUM &&
72+
x[1] !== BADNUM && y[1] !== BADNUM &&
73+
x[2] !== BADNUM && y[2] !== BADNUM &&
74+
x[3] !== BADNUM && y[3] !== BADNUM
75+
) {
7076
if(isHorizontal) {
7177
shape += 'M' + x[0] + ',' + y[1] + 'L' + x[2] + ',' + y[2] + 'H' + x[3] + 'L' + x[1] + ',' + y[1] + 'Z';
7278
} else {
7379
shape += 'M' + x[1] + ',' + y[1] + 'L' + x[2] + ',' + y[3] + 'V' + y[2] + 'L' + x[1] + ',' + y[0] + 'Z';
7480
}
7581
}
7682

83+
if(shape === '') shape = 'M0,0Z';
84+
7785
Lib.ensureSingle(d3.select(this), 'path')
7886
.attr('d', shape)
7987
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);

src/traces/waterfall/plot.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var d3 = require('d3');
1212
var Lib = require('../../lib');
1313
var Drawing = require('../../components/drawing');
14+
var BADNUM = require('../../constants/numerical').BADNUM;
1415
var barPlot = require('../bar/plot');
1516
var clearMinTextSize = require('../bar/uniform_text').clearMinTextSize;
1617

@@ -66,31 +67,36 @@ function plotConnectors(gd, plotinfo, cdModule, traceLayer) {
6667

6768
var shape = '';
6869

69-
if(mode === 'spanning') {
70-
if(!di.isSum && i > 0) {
71-
if(isHorizontal) {
72-
shape += 'M' + x[0] + ',' + y[1] + 'V' + y[0];
73-
} else {
74-
shape += 'M' + x[1] + ',' + y[0] + 'H' + x[0];
70+
if(
71+
x[0] !== BADNUM && y[0] !== BADNUM &&
72+
x[1] !== BADNUM && y[1] !== BADNUM
73+
) {
74+
if(mode === 'spanning') {
75+
if(!di.isSum && i > 0) {
76+
if(isHorizontal) {
77+
shape += 'M' + x[0] + ',' + y[1] + 'V' + y[0];
78+
} else {
79+
shape += 'M' + x[1] + ',' + y[0] + 'H' + x[0];
80+
}
7581
}
7682
}
77-
}
7883

79-
if(mode !== 'between') {
80-
if(di.isSum || i < len - 1) {
81-
if(isHorizontal) {
82-
shape += 'M' + x[1] + ',' + y[0] + 'V' + y[1];
83-
} else {
84-
shape += 'M' + x[0] + ',' + y[1] + 'H' + x[1];
84+
if(mode !== 'between') {
85+
if(di.isSum || i < len - 1) {
86+
if(isHorizontal) {
87+
shape += 'M' + x[1] + ',' + y[0] + 'V' + y[1];
88+
} else {
89+
shape += 'M' + x[0] + ',' + y[1] + 'H' + x[1];
90+
}
8591
}
8692
}
87-
}
8893

89-
if(x[2] !== undefined && y[2] !== undefined) {
90-
if(isHorizontal) {
91-
shape += 'M' + x[1] + ',' + y[1] + 'V' + y[2];
92-
} else {
93-
shape += 'M' + x[1] + ',' + y[1] + 'H' + x[2];
94+
if(x[2] !== BADNUM && y[2] !== BADNUM) {
95+
if(isHorizontal) {
96+
shape += 'M' + x[1] + ',' + y[1] + 'V' + y[2];
97+
} else {
98+
shape += 'M' + x[1] + ',' + y[1] + 'H' + x[2];
99+
}
94100
}
95101
}
96102

test/jasmine/tests/bar_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2932,7 +2932,7 @@ describe('bar tweening', function() {
29322932
.then(done);
29332933
});
29342934

2935-
it('handle NaN positions on vertical bars', function(done) {
2935+
it('handle BADNUM positions on vertical bars', function(done) {
29362936
var y1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
29372937
var y2 = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
29382938
var mockCopy = {

test/jasmine/tests/funnel_test.js

+49
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var rgb = color.rgb;
1717
var customAssertions = require('../assets/custom_assertions');
1818
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
1919
var checkTextTemplate = require('../assets/check_texttemplate');
20+
var checkTransition = require('../assets/check_transitions');
2021
var Fx = require('@src/components/fx');
2122

2223
var d3 = require('d3');
@@ -1033,6 +1034,54 @@ describe('A funnel plot', function() {
10331034
.then(done);
10341035
});
10351036

1037+
it('handle BADNUM positions', function(done) {
1038+
var x1 = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
1039+
var x2 = x1; // no transition now
1040+
var mockCopy = {
1041+
data: [
1042+
{
1043+
type: 'funnel',
1044+
y: [
1045+
0,
1046+
1,
1047+
'',
1048+
'NaN',
1049+
NaN,
1050+
Infinity,
1051+
-Infinity,
1052+
undefined,
1053+
null,
1054+
9,
1055+
10
1056+
],
1057+
x: x1
1058+
}
1059+
],
1060+
layout: {
1061+
width: 800,
1062+
height: 600
1063+
}
1064+
};
1065+
1066+
var barTests = [
1067+
[0, '.point path', 'attr', 'd', ['M245,4V34H395V4Z', 'M251,42V73H389V42Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M306,347V378H334V347Z', 'M313,386V416H327V386Z']]
1068+
];
1069+
1070+
var connectorTests = [
1071+
[0, '.regions path', 'attr', 'd', ['M245,34L251,42H389L395,34Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M306,378L313,386H327L334,378Z', 'M0,0Z']]
1072+
];
1073+
1074+
var animateOpts = {data: [{x: x2}]};
1075+
var transitionOpts = false; // use default
1076+
1077+
checkTransition(gd, mockCopy, animateOpts, transitionOpts, barTests)
1078+
.then(function() {
1079+
return checkTransition(gd, mockCopy, animateOpts, transitionOpts, connectorTests);
1080+
})
1081+
.catch(failTest)
1082+
.then(done);
1083+
});
1084+
10361085
it('should be able to deal with transform that empty out the data coordinate arrays', function(done) {
10371086
Plotly.plot(gd, {
10381087
data: [{

test/jasmine/tests/waterfall_test.js

+49
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var rgb = color.rgb;
1717
var customAssertions = require('../assets/custom_assertions');
1818
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
1919
var checkTextTemplate = require('../assets/check_texttemplate');
20+
var checkTransition = require('../assets/check_transitions');
2021
var Fx = require('@src/components/fx');
2122

2223
var d3 = require('d3');
@@ -977,6 +978,54 @@ describe('A waterfall plot', function() {
977978
.then(done);
978979
});
979980

981+
it('handle BADNUM positions', function(done) {
982+
var y1 = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
983+
var y2 = y1; // no transition now
984+
var mockCopy = {
985+
data: [
986+
{
987+
type: 'waterfall',
988+
x: [
989+
0,
990+
1,
991+
'',
992+
'NaN',
993+
NaN,
994+
Infinity,
995+
-Infinity,
996+
undefined,
997+
null,
998+
9,
999+
10
1000+
],
1001+
y: y1
1002+
}
1003+
],
1004+
layout: {
1005+
width: 400,
1006+
height: 300
1007+
}
1008+
};
1009+
1010+
var barTests = [
1011+
[0, '.point path', 'attr', 'd', ['M2,121V109H20V121Z', 'M24,111V98H41V111Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M199,28V15H216V28Z', 'M220,17V5H238V17Z']]
1012+
];
1013+
1014+
var connectorTests = [
1015+
[0, '.line path', 'attr', 'd', ['M20,110H24', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M0,0Z', 'M216,16H220', 'M0,0Z']]
1016+
];
1017+
1018+
var animateOpts = {data: [{y: y2}]};
1019+
var transitionOpts = false; // use default
1020+
1021+
checkTransition(gd, mockCopy, animateOpts, transitionOpts, barTests)
1022+
.then(function() {
1023+
return checkTransition(gd, mockCopy, animateOpts, transitionOpts, connectorTests);
1024+
})
1025+
.catch(failTest)
1026+
.then(done);
1027+
});
1028+
9801029
it('should be able to deal with transform that empty out the data coordinate arrays', function(done) {
9811030
Plotly.plot(gd, {
9821031
data: [{

0 commit comments

Comments
 (0)