Skip to content

Commit 536ba6f

Browse files
authored
Merge pull request #2280 from plotly/ghost-fill
fix ghost fill-tonext when either trace is emptied out
2 parents defb8aa + fb8357a commit 536ba6f

File tree

3 files changed

+79
-21
lines changed

3 files changed

+79
-21
lines changed

src/traces/scatter/link_traces.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@
99
'use strict';
1010

1111
module.exports = function linkTraces(gd, plotinfo, cdscatter) {
12-
var cd, trace;
12+
var trace, i;
1313
var prevtrace = null;
1414

15-
for(var i = 0; i < cdscatter.length; ++i) {
16-
cd = cdscatter[i];
17-
trace = cd[0].trace;
15+
for(i = 0; i < cdscatter.length; ++i) {
16+
trace = cdscatter[i][0].trace;
1817

1918
// Note: The check which ensures all cdscatter here are for the same axis and
2019
// are either cartesian or scatterternary has been removed. This code assumes

src/traces/scatter/plot.js

+32-17
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
323323

324324
Drawing.setClipUrl(lineJoin, plotinfo.layerClipId);
325325

326+
function clearFill(selection) {
327+
transition(selection).attr('d', 'M0,0Z');
328+
}
329+
326330
if(segments.length) {
327331
if(ownFillEl3) {
328332
if(pt0 && pt1) {
@@ -348,30 +352,41 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
348352
}
349353
}
350354
}
351-
else if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
352-
// fill to next: full trace path, plus the previous path reversed
353-
if(trace.fill === 'tonext') {
354-
// tonext: for use by concentric shapes, like manually constructed
355-
// contours, we just add the two paths closed on themselves.
356-
// This makes strange results if one path is *not* entirely
357-
// inside the other, but then that is a strange usage.
358-
transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
359-
.call(Drawing.singleFillStyle);
355+
else if(tonext) {
356+
if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
357+
// fill to next: full trace path, plus the previous path reversed
358+
if(trace.fill === 'tonext') {
359+
// tonext: for use by concentric shapes, like manually constructed
360+
// contours, we just add the two paths closed on themselves.
361+
// This makes strange results if one path is *not* entirely
362+
// inside the other, but then that is a strange usage.
363+
transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
364+
.call(Drawing.singleFillStyle);
365+
}
366+
else {
367+
// tonextx/y: for now just connect endpoints with lines. This is
368+
// the correct behavior if the endpoints are at the same value of
369+
// y/x, but if they *aren't*, we should ideally do more complicated
370+
// things depending on whether the new endpoint projects onto the
371+
// existing curve or off the end of it
372+
transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
373+
.call(Drawing.singleFillStyle);
374+
}
375+
trace._polygons = trace._polygons.concat(prevPolygons);
360376
}
361377
else {
362-
// tonextx/y: for now just connect endpoints with lines. This is
363-
// the correct behavior if the endpoints are at the same value of
364-
// y/x, but if they *aren't*, we should ideally do more complicated
365-
// things depending on whether the new endpoint projects onto the
366-
// existing curve or off the end of it
367-
transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
368-
.call(Drawing.singleFillStyle);
378+
clearFill(tonext);
379+
trace._polygons = null;
369380
}
370-
trace._polygons = trace._polygons.concat(prevPolygons);
371381
}
372382
trace._prevRevpath = revpath;
373383
trace._prevPolygons = thisPolygons;
374384
}
385+
else {
386+
if(ownFillEl3) clearFill(ownFillEl3);
387+
else if(tonext) clearFill(tonext);
388+
trace._polygons = trace._prevRevpath = trace._prevPolygons = null;
389+
}
375390

376391

377392
function visFilter(d) {

test/jasmine/tests/scatter_test.js

+44
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,50 @@ describe('end-to-end scatter tests', function() {
713713
expect(fill()).toEqual('rgb(0, 0, 255)');
714714
}).catch(fail).then(done);
715715
});
716+
717+
it('clears fills tonext when either trace is emptied out', function(done) {
718+
var trace0 = {y: [1, 3, 5, 7]};
719+
var trace1 = {y: [1, 2, 3, 4], line: {width: 0}, mode: 'lines'};
720+
var trace2 = {y: [3, 4, 5, 6], fill: 'tonexty', mode: 'none'};
721+
722+
function checkFill(visible, msg) {
723+
var fillSelection = d3.select(gd).selectAll('.scatterlayer .js-fill');
724+
expect(fillSelection.size()).toBe(1, msg);
725+
expect(fillSelection.attr('d')).negateIf(visible).toBe('M0,0Z', msg);
726+
}
727+
728+
Plotly.newPlot(gd, [trace0, trace1, trace2], {}, {scrollZoom: true})
729+
.then(function() {
730+
checkFill(true, 'initial');
731+
732+
return Plotly.restyle(gd, {y: [[null, null, null, null]]}, [1]);
733+
})
734+
.then(function() {
735+
checkFill(false, 'null out trace 1');
736+
737+
return Plotly.restyle(gd, {y: [[1, 2, 3, 4]]}, [1]);
738+
})
739+
.then(function() {
740+
checkFill(true, 'restore trace 1');
741+
742+
return Plotly.restyle(gd, {y: [[null, null, null, null]]}, [2]);
743+
})
744+
.then(function() {
745+
checkFill(false, 'null out trace 2');
746+
747+
return Plotly.restyle(gd, {y: [[1, 2, 3, 4]]}, [2]);
748+
})
749+
.then(function() {
750+
checkFill(true, 'restore trace 2');
751+
752+
return Plotly.restyle(gd, {y: [[null, null, null, null], [null, null, null, null]]}, [1, 2]);
753+
})
754+
.then(function() {
755+
checkFill(false, 'null out both traces');
756+
})
757+
.catch(fail)
758+
.then(done);
759+
});
716760
});
717761

718762
describe('scatter hoverPoints', function() {

0 commit comments

Comments
 (0)