Skip to content

Commit 69cb628

Browse files
committed
fix #3943 - don't mutate fullLayout ax ranges during zoombox mousemove
- first reason, why don't need to - second reason, mutation aren't reset meaning going from 2d back to 1d zoombox lead to wrong rendering after mouseup and a discrepency between gd.layout(x|y).range and gd._fullLayout(x|y).range values
1 parent 3a8cf51 commit 69cb628

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

src/plots/cartesian/dragbox.js

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
8585
// graph-wide optimization flags
8686
var hasScatterGl, hasSplom, hasSVG;
8787
// collected changes to be made to the plot by relayout at the end
88-
var updates = {};
88+
var updates;
8989

9090
function recomputeAxisLists() {
9191
xa0 = plotinfo.xaxis;
@@ -415,6 +415,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
415415
}
416416

417417
function computeZoomUpdates() {
418+
updates = {};
419+
418420
// TODO: edit linked axes in zoomAxRanges and in dragTail
419421
if(zoomMode === 'xy' || zoomMode === 'x') {
420422
zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes);
@@ -427,8 +429,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
427429
}
428430

429431
function zoomDone() {
430-
updates = {};
431-
432432
// more strict than dragged, which allows you to come back to where you started
433433
// and still count as dragged
434434
if(Math.min(box.h, box.w) < MINDRAG * 2) {
@@ -654,12 +654,12 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
654654
var ax2 = constrainedAxes[0] || xaHash[axId2] || yaHash[axId2];
655655

656656
if(ax2) {
657-
var rng = ax2.range;
658657
if(out) {
659-
out[ax._name + '.range[0]'] = rng[0];
660-
out[ax._name + '.range[1]'] = rng[1];
658+
// zoombox case - don't mutate 'range', just add keys in 'updates'
659+
out[ax._name + '.range[0]'] = out[ax2._name + '.range[0]'];
660+
out[ax._name + '.range[1]'] = out[ax2._name + '.range[1]'];
661661
} else {
662-
ax.range = rng;
662+
ax.range = ax2.range.slice();
663663
}
664664
}
665665
}
@@ -995,19 +995,14 @@ function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
995995

996996
var axRangeLinear0 = axi._rl[0];
997997
var axRangeLinearSpan = axi._rl[1] - axRangeLinear0;
998-
axi.range = [
999-
axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction),
1000-
axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction)
1001-
];
1002-
1003-
updates[axi._name + '.range[0]'] = axi.range[0];
1004-
updates[axi._name + '.range[1]'] = axi.range[1];
998+
updates[axi._name + '.range[0]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction);
999+
updates[axi._name + '.range[1]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction);
10051000
}
10061001

10071002
// zoom linked axes about their centers
10081003
if(linkedAxes && linkedAxes.length) {
10091004
var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;
1010-
zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, [], []);
1005+
zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, []);
10111006
}
10121007
}
10131008

test/jasmine/tests/cartesian_interact_test.js

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,72 @@ describe('axis zoom/pan and main plot zoom', function() {
727727
.then(done);
728728
});
729729

730+
it('handles y-only to xy back to y-only in single zoombox drag motion', function(done) {
731+
function _assert(msg, evtData, xrng, yrng) {
732+
expect([evtData['xaxis.range[0]'], evtData['xaxis.range[1]']])
733+
.toBeCloseToArray(xrng, 2, 'x evt - ' + msg);
734+
expect([evtData['yaxis.range[0]'], evtData['yaxis.range[1]']])
735+
.toBeCloseToArray(yrng, 2, 'y evt - ' + msg);
736+
}
737+
738+
var relayoutingList = [];
739+
var relayoutList = [];
740+
741+
var xrng0 = [-0.1347, 2.1347];
742+
var yrng1 = [1.3581, 1.5];
743+
var blank = [undefined, undefined];
744+
745+
Plotly.plot(gd, [{
746+
y: [1, 2, 1]
747+
}], {
748+
margin: {l: 0, t: 0, r: 0, b: 0},
749+
width: 400, height: 400
750+
})
751+
.then(function() {
752+
gd.on('plotly_relayouting', function(d) {
753+
relayoutingList.push(d);
754+
755+
// N.B. should not mutate axis range on mousemove
756+
expect(gd._fullLayout.xaxis.range)
757+
.toBeCloseToArray(xrng0, 2, 'full x range| relyouting call #' + relayoutingList.length);
758+
});
759+
gd.on('plotly_relayout', function(d) { relayoutList.push(d); });
760+
})
761+
.then(function() {
762+
return drag({
763+
node: getDragger('xy', 'nsew'),
764+
path: [
765+
// start in middle
766+
[200, 200],
767+
// y-only zoombox
768+
[200, 250],
769+
// xy zoombox
770+
[250, 250],
771+
// back to y-only
772+
[200, 250]
773+
]
774+
});
775+
})
776+
.then(delay(100))
777+
.then(function() {
778+
if(relayoutingList.length === 3) {
779+
_assert('relayouting y-only', relayoutingList[0], blank, yrng1);
780+
_assert('relayouting xy', relayoutingList[1], [0.9999, 1.2836], yrng1);
781+
_assert('relayouting back to y-only', relayoutingList[2], blank, yrng1);
782+
} else {
783+
fail('did not emit correct number of plotly_relayouting events');
784+
}
785+
786+
if(relayoutList.length === 1) {
787+
_assert('relayout', relayoutList[0], blank, yrng1);
788+
} else {
789+
fail('did not emit correct number of plotly_relayout events');
790+
}
791+
})
792+
.catch(failTest)
793+
.then(done);
794+
});
795+
730796
it('should compute correct multicategory tick label span during drag', function(done) {
731797
var fig = Lib.extendDeep({}, require('@mocks/multicategory.json'));
732798

@@ -1545,15 +1611,16 @@ describe('axis zoom/pan and main plot zoom', function() {
15451611
return drag.start()
15461612
.then(_assert('just after start of zoombox', {
15471613
nodeCnt: 4,
1548-
xrng: [1.5, 1.6880],
1614+
xrng: 'previous',
15491615
hasDragData: true,
15501616
zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z',
15511617
clipTranslate: [0, 0]
15521618
}))
15531619
.then(delay(step))
15541620
.then(_assert('during zoombox drag', {
15551621
nodeCnt: 5,
1556-
xrng: [2, 2.2507],
1622+
// N.B. x autorange for one more node
1623+
xrng: [-0.257, 4.257],
15571624
hasDragData: true,
15581625
zoombox: 'M269.5,114.5h-3v41h3ZM300.5,114.5h3v41h-3Z',
15591626
clipTranslate: [0, 0]

0 commit comments

Comments
 (0)