From aa1ec8a1d774105f8d70fb5607d2c7abdc9bba4f Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Wed, 2 May 2018 20:08:27 -0400 Subject: [PATCH 01/13] Generate events while dragging. --- src/plots/gl3d/scene.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 2729b8d72f2..403ecf48311 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -192,6 +192,7 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { }; scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene)); + scene.glplot.canvas.addEventListener('mousemove', relayoutCallback.bind(null, scene)); scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false); if(!scene.staticMode) { From 851bcd6094bc3ca4cfd1d1318447dd5836dd8ea0 Mon Sep 17 00:00:00 2001 From: etienne Date: Thu, 24 May 2018 11:39:33 -0400 Subject: [PATCH 02/13] sub fail -> failTest --- test/jasmine/tests/gl2d_plot_interact_test.js | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index dc9ece04979..5f92ca40f4b 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -8,7 +8,7 @@ var ScatterGl = require('@src/traces/scattergl'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var fail = require('../assets/fail_test'); +var failTest = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var touchEvent = require('../assets/touch_event'); var drag = require('../assets/drag'); @@ -202,7 +202,7 @@ describe('@gl Test gl plot side effects', function() { .then(function() { expect(gd.querySelector('.gl-canvas-context').width).toBe(300); }) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -350,7 +350,7 @@ describe('@gl Test gl2d plots', function() { 'yaxis.range[1]': jasmine.any(Number) })); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -381,7 +381,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 108, 100)[0]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -423,7 +423,7 @@ describe('@gl Test gl2d plots', function() { expect(scene.error2d.draw).toHaveBeenCalledTimes(2, 'twice for x AND y'); expect(scene.scatter2d.draw).toHaveBeenCalledTimes(3, 'both traces have markers'); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -451,7 +451,7 @@ describe('@gl Test gl2d plots', function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0); expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -490,7 +490,7 @@ describe('@gl Test gl2d plots', function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 158, 100)[3]).not.toBe(0); expect(readPixel(gd.querySelector('.gl-canvas-focus'), 168, 100)[3]).not.toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -514,7 +514,7 @@ describe('@gl Test gl2d plots', function() { expect(countCanvases()).toBe(0); expect(d3.selectAll('.scatterlayer > .trace').size()).toBe(1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -567,7 +567,7 @@ describe('@gl Test gl2d plots', function() { expect(gd.layout.xaxis.range).toBeCloseToArray([6, 8], 3); expect(gd.layout.yaxis.range).toBeCloseToArray([5, 7], 3); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -634,7 +634,7 @@ describe('@gl Test gl2d plots', function() { expect(gd.layout.xaxis.range).toBeCloseToArray([-8.2, 24.2], 1); expect(gd.layout.yaxis.range).toBeCloseToArray([-0.12, 16.1], 1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -690,7 +690,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { assertAnnotation([327, 331]); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -744,7 +744,7 @@ describe('@gl Test gl2d plots', function() { expect(relayoutCallback).toHaveBeenCalledTimes(1); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -768,7 +768,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(ScatterGl.calc).toHaveBeenCalledTimes(2); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -852,7 +852,7 @@ describe('@gl Test gl2d plots', function() { expect(scene.selectBatch).toBe(null, msg); expect(scene.unselectBatch).toBe(null, msg); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -869,7 +869,7 @@ describe('@gl Test gl2d plots', function() { .then(function() { expect(readPixel(gd.querySelector('.gl-canvas-context'), 100, 80)[0]).toBe(0); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -914,7 +914,7 @@ describe('@gl Test gl2d plots', function() { // and 105545275 after. expect(total).toBeGreaterThan(4e6); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -945,7 +945,7 @@ describe('@gl Test gl2d plots', function() { expect(opts.positions) .toBeCloseToArray([1, 1, 2, 2, 3, 1]); }) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -987,7 +987,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray(glRangeX, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray(glRangeY, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -1026,7 +1026,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.079, 1.079], 2, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray([-0.105, 1.105], 2, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -1044,7 +1044,7 @@ describe('Test scattergl autorange:', function() { expect(gd._fullLayout.xaxis.range).toBeCloseToArray([-0.119, 1.119], 2, 'x range'); expect(gd._fullLayout.yaxis.range).toBeCloseToArray([-0.199, 1.199], 2, 'y range'); }) - .catch(fail) + .catch(failTest) .then(done); }); }); From 0d523d408873ac5b5c5cdecfc5177213d60be445 Mon Sep 17 00:00:00 2001 From: etienne Date: Thu, 24 May 2018 11:48:44 -0400 Subject: [PATCH 03/13] do not create WebGL ctx in 'pick' canvas on graphs w/o parcoords - so that we create two (not three) contexts in scattergl graph as we in versions before 1.36.0 --- src/lib/prepare_regl.js | 6 +++++- test/jasmine/tests/gl2d_plot_interact_test.js | 14 ++++++++++++++ test/jasmine/tests/parcoords_test.js | 12 ++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/lib/prepare_regl.js b/src/lib/prepare_regl.js index 3c6ca297083..228d134208a 100644 --- a/src/lib/prepare_regl.js +++ b/src/lib/prepare_regl.js @@ -23,8 +23,12 @@ var createRegl = require('regl'); * @param {array} extensions : list of extension to pass to createRegl */ module.exports = function prepareRegl(gd, extensions) { - gd._fullLayout._glcanvas.each(function(d) { + var fullLayout = gd._fullLayout; + + fullLayout._glcanvas.each(function(d) { if(d.regl) return; + // only parcoords needs pick layer + if(d.pick && !fullLayout._has('parcoords')) return; d.regl = createRegl({ canvas: this, diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 5f92ca40f4b..e79ac5d5582 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -948,6 +948,20 @@ describe('@gl Test gl2d plots', function() { .catch(failTest) .then(done); }); + + it('should create two WebGL context per graph', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_stacked_subplots.json')); + + Plotly.plot(gd, fig).then(function() { + var cnt = 0; + d3.select(gd).selectAll('canvas').each(function(d) { + if(d.regl) cnt++; + }); + expect(cnt).toBe(2); + }) + .catch(failTest) + .then(done); + }); }); describe('Test scattergl autorange:', function() { diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index 06a65999815..f92ea036a43 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -770,8 +770,8 @@ describe('@gl parcoords Lifecycle methods', function() { }); describe('@gl parcoords basic use', function() { - var mockCopy, - gd; + var mockCopy; + var gd; beforeEach(function(done) { mockCopy = Lib.extendDeep({}, mock); @@ -789,6 +789,14 @@ describe('@gl parcoords basic use', function() { afterAll(purgeGraphDiv); + it('should create three WebGL context per graph', function() { + var cnt = 0; + d3.select(gd).selectAll('canvas').each(function(d) { + if(d.regl) cnt++; + }); + expect(cnt).toBe(3); + }); + it('`Plotly.plot` should have proper fields on `gd.data` on initial rendering', function() { expect(gd.data.length).toEqual(1); From a3dce1299c31d9f38c4addcf9e6ff953855bb870 Mon Sep 17 00:00:00 2001 From: etienne Date: Thu, 24 May 2018 13:04:32 -0400 Subject: [PATCH 04/13] grammar :goat: --- test/jasmine/tests/gl2d_plot_interact_test.js | 2 +- test/jasmine/tests/parcoords_test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index e79ac5d5582..15f5c92169d 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -949,7 +949,7 @@ describe('@gl Test gl2d plots', function() { .then(done); }); - it('should create two WebGL context per graph', function(done) { + it('should create two WebGL contexts per graph', function(done) { var fig = Lib.extendDeep({}, require('@mocks/gl2d_stacked_subplots.json')); Plotly.plot(gd, fig).then(function() { diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index f92ea036a43..a4ea3e41e86 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -789,7 +789,7 @@ describe('@gl parcoords basic use', function() { afterAll(purgeGraphDiv); - it('should create three WebGL context per graph', function() { + it('should create three WebGL contexts per graph', function() { var cnt = 0; d3.select(gd).selectAll('canvas').each(function(d) { if(d.regl) cnt++; From 0cad1fee0323ce7fe4d62cea657668a8f6a990e3 Mon Sep 17 00:00:00 2001 From: etienne Date: Fri, 25 May 2018 12:53:37 -0400 Subject: [PATCH 05/13] lint (breakup prepFn and clickFn from dragOption obj assigment) --- src/plots/cartesian/dragbox.js | 168 +++++++++++++++++---------------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ebd542fdbdb..b83cfa2c42e 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -144,98 +144,100 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { var dragOptions = { element: dragger, gd: gd, - plotinfo: plotinfo, - prepFn: function(e, startX, startY) { - var dragModeNow = gd._fullLayout.dragmode; - - recomputeAxisLists(); - - if(!allFixedRanges) { - if(isMainDrag) { - // main dragger handles all drag modes, and changes - // to pan (or to zoom if it already is pan) on shift - if(e.shiftKey) { - if(dragModeNow === 'pan') dragModeNow = 'zoom'; - else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan'; - } - else if(e.ctrlKey) { - dragModeNow = 'pan'; - } + plotinfo: plotinfo + }; + + dragOptions.prepFn = function(e, startX, startY) { + var dragModeNow = gd._fullLayout.dragmode; + + recomputeAxisLists(); + + if(!allFixedRanges) { + if(isMainDrag) { + // main dragger handles all drag modes, and changes + // to pan (or to zoom if it already is pan) on shift + if(e.shiftKey) { + if(dragModeNow === 'pan') dragModeNow = 'zoom'; + else if(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan'; + } + else if(e.ctrlKey) { + dragModeNow = 'pan'; } - // all other draggers just pan - else dragModeNow = 'pan'; } + // all other draggers just pan + else dragModeNow = 'pan'; + } - if(dragModeNow === 'lasso') dragOptions.minDrag = 1; - else dragOptions.minDrag = undefined; + if(dragModeNow === 'lasso') dragOptions.minDrag = 1; + else dragOptions.minDrag = undefined; - if(isSelectOrLasso(dragModeNow)) { - dragOptions.xaxes = xaxes; - dragOptions.yaxes = yaxes; - prepSelect(e, startX, startY, dragOptions, dragModeNow); - } - else if(allFixedRanges) { - clearSelect(zoomlayer); - } - else if(dragModeNow === 'zoom') { - dragOptions.moveFn = zoomMove; - dragOptions.doneFn = zoomDone; + if(isSelectOrLasso(dragModeNow)) { + dragOptions.xaxes = xaxes; + dragOptions.yaxes = yaxes; + prepSelect(e, startX, startY, dragOptions, dragModeNow); + } + else if(allFixedRanges) { + clearSelect(zoomlayer); + } + else if(dragModeNow === 'zoom') { + dragOptions.moveFn = zoomMove; + dragOptions.doneFn = zoomDone; - // zoomMove takes care of the threshold, but we need to - // minimize this so that constrained zoom boxes will flip - // orientation at the right place - dragOptions.minDrag = 1; + // zoomMove takes care of the threshold, but we need to + // minimize this so that constrained zoom boxes will flip + // orientation at the right place + dragOptions.minDrag = 1; - zoomPrep(e, startX, startY); - } - else if(dragModeNow === 'pan') { - dragOptions.moveFn = plotDrag; - dragOptions.doneFn = dragTail; - clearSelect(zoomlayer); - } - }, - clickFn: function(numClicks, evt) { - removeZoombox(gd); + zoomPrep(e, startX, startY); + } + else if(dragModeNow === 'pan') { + dragOptions.moveFn = plotDrag; + dragOptions.doneFn = dragTail; + clearSelect(zoomlayer); + } + }; - if(numClicks === 2 && !singleEnd) doubleClick(); + dragOptions.clickFn = function(numClicks, evt) { + removeZoombox(gd); - if(isMainDrag) { - Fx.click(gd, evt, plotinfo.id); + if(numClicks === 2 && !singleEnd) doubleClick(); + + if(isMainDrag) { + Fx.click(gd, evt, plotinfo.id); + } + else if(numClicks === 1 && singleEnd) { + var ax = ns ? ya0 : xa0, + end = (ns === 's' || ew === 'w') ? 0 : 1, + attrStr = ax._name + '.range[' + end + ']', + initialText = getEndText(ax, end), + hAlign = 'left', + vAlign = 'middle'; + + if(ax.fixedrange) return; + + if(ns) { + vAlign = (ns === 'n') ? 'top' : 'bottom'; + if(ax.side === 'right') hAlign = 'right'; } - else if(numClicks === 1 && singleEnd) { - var ax = ns ? ya0 : xa0, - end = (ns === 's' || ew === 'w') ? 0 : 1, - attrStr = ax._name + '.range[' + end + ']', - initialText = getEndText(ax, end), - hAlign = 'left', - vAlign = 'middle'; - - if(ax.fixedrange) return; - - if(ns) { - vAlign = (ns === 'n') ? 'top' : 'bottom'; - if(ax.side === 'right') hAlign = 'right'; - } - else if(ew === 'e') hAlign = 'right'; - - if(gd._context.showAxisRangeEntryBoxes) { - d3.select(dragger) - .call(svgTextUtils.makeEditable, { - gd: gd, - immediate: true, - background: gd._fullLayout.paper_bgcolor, - text: String(initialText), - fill: ax.tickfont ? ax.tickfont.color : '#444', - horizontalAlign: hAlign, - verticalAlign: vAlign - }) - .on('edit', function(text) { - var v = ax.d2r(text); - if(v !== undefined) { - Registry.call('relayout', gd, attrStr, v); - } - }); - } + else if(ew === 'e') hAlign = 'right'; + + if(gd._context.showAxisRangeEntryBoxes) { + d3.select(dragger) + .call(svgTextUtils.makeEditable, { + gd: gd, + immediate: true, + background: gd._fullLayout.paper_bgcolor, + text: String(initialText), + fill: ax.tickfont ? ax.tickfont.color : '#444', + horizontalAlign: hAlign, + verticalAlign: vAlign + }) + .on('edit', function(text) { + var v = ax.d2r(text); + if(v !== undefined) { + Registry.call('relayout', gd, attrStr, v); + } + }); } } }; From 20820cb70c4879c91e8ade621c829f1b5ac4c069 Mon Sep 17 00:00:00 2001 From: etienne Date: Fri, 25 May 2018 14:36:41 -0400 Subject: [PATCH 06/13] reassign zoom/pan clickFn when switching from select/lasso mode --- src/plots/cartesian/dragbox.js | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index b83cfa2c42e..e81be8142f3 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -174,30 +174,32 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(isSelectOrLasso(dragModeNow)) { dragOptions.xaxes = xaxes; dragOptions.yaxes = yaxes; + // this attaches moveFn, clickFn, doneFn on dragOptions prepSelect(e, startX, startY, dragOptions, dragModeNow); - } - else if(allFixedRanges) { - clearSelect(zoomlayer); - } - else if(dragModeNow === 'zoom') { - dragOptions.moveFn = zoomMove; - dragOptions.doneFn = zoomDone; - - // zoomMove takes care of the threshold, but we need to - // minimize this so that constrained zoom boxes will flip - // orientation at the right place - dragOptions.minDrag = 1; - - zoomPrep(e, startX, startY); - } - else if(dragModeNow === 'pan') { - dragOptions.moveFn = plotDrag; - dragOptions.doneFn = dragTail; - clearSelect(zoomlayer); + } else { + dragOptions.clickFn = clickFn; + + if(allFixedRanges) { + clearSelect(zoomlayer); + } else if(dragModeNow === 'zoom') { + dragOptions.moveFn = zoomMove; + dragOptions.doneFn = zoomDone; + + // zoomMove takes care of the threshold, but we need to + // minimize this so that constrained zoom boxes will flip + // orientation at the right place + dragOptions.minDrag = 1; + + zoomPrep(e, startX, startY); + } else if(dragModeNow === 'pan') { + dragOptions.moveFn = plotDrag; + dragOptions.doneFn = dragTail; + clearSelect(zoomlayer); + } } }; - dragOptions.clickFn = function(numClicks, evt) { + function clickFn(numClicks, evt) { removeZoombox(gd); if(numClicks === 2 && !singleEnd) doubleClick(); @@ -240,7 +242,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { }); } } - }; + } dragElement.init(dragOptions); From 1be1742bb2bd9c6d5c59ce20eb47f6726b496041 Mon Sep 17 00:00:00 2001 From: etienne Date: Fri, 25 May 2018 14:37:08 -0400 Subject: [PATCH 07/13] add select vs pan double-click test --- test/jasmine/tests/select_test.js | 108 +++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 1a0e6323e97..cd9f60d03cf 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -612,9 +612,115 @@ describe('@flaky Test select box and lasso in general:', function() { }) .catch(failTest) .then(done); - }); + it('should clear selected points on double click only on pan/lasso modes', function(done) { + var gd = createGraphDiv(); + var fig = Lib.extendDeep({}, require('@mocks/0.json')); + fig.data = [fig.data[0]]; + fig.layout.xaxis.autorange = false; + fig.layout.xaxis.range = [2, 8]; + fig.layout.yaxis.autorange = false; + fig.layout.yaxis.range = [0, 3]; + + function _assert(msg, exp) { + expect(gd.layout.xaxis.range) + .toBeCloseToArray(exp.xrng, 2, 'xaxis range - ' + msg); + expect(gd.layout.yaxis.range) + .toBeCloseToArray(exp.yrng, 2, 'yaxis range - ' + msg); + + if(exp.selpts === null) { + expect('selectedpoints' in gd.data[0]) + .toBe(false, 'cleared selectedpoints - ' + msg); + } else { + expect(gd.data[0].selectedpoints) + .toBeCloseToArray(exp.selpts, 2, 'selectedpoints - ' + msg); + } + } + + Plotly.plot(gd, fig).then(function() { + _assert('base', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return Plotly.relayout(gd, 'xaxis.range', [0, 10]); + }) + .then(function() { + _assert('after xrng relayout', { + xrng: [0, 10], + yrng: [0, 3], + selpts: null + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode zoom', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return Plotly.relayout(gd, 'dragmode', 'select'); + }) + .then(function() { + _assert('after relayout to select', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return drag([[100, 100], [400, 400]]); + }) + .then(function() { + _assert('after selection', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode select', { + xrng: [2, 8], + yrng: [0, 3], + selpts: null + }); + return drag([[100, 100], [400, 400]]); + }) + .then(function() { + _assert('after selection 2', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return Plotly.relayout(gd, 'dragmode', 'pan'); + }) + .then(function() { + _assert('after relayout to pan', { + xrng: [2, 8], + yrng: [0, 3], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return Plotly.relayout(gd, 'yaxis.range', [0, 20]); + }) + .then(function() { + _assert('after yrng relayout', { + xrng: [2, 8], + yrng: [0, 20], + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + return doubleClick(200, 200); + }) + .then(function() { + _assert('after double-click under dragmode pan', { + xrng: [2, 8], + yrng: [0, 3], + // N.B. does not clear selection! + selpts: [40, 41, 42, 43, 44, 45, 46, 47, 48] + }); + }) + .catch(failTest) + .then(done); + }); }); describe('@flaky Test select box and lasso per trace:', function() { From 3460248dfb278fb7cca1715ee8e3e846fb3e69fc Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Mon, 28 May 2018 13:38:07 -0400 Subject: [PATCH 08/13] Adding relayouting and relayout events for cartesian plots. --- src/plots/cartesian/dragbox.js | 47 ++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index ebd542fdbdb..78d4719dce6 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -349,6 +349,38 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { gd._dragged = zoomDragged; updateZoombox(zb, corners, box, path0, dimmed, lum); + // what event data do we emit here? In gl3d, camera location is emitted. + // what is needed for relayouting a cartesian plot? + // for plotly_relayout, the payload is always 'updates' + // updates[ax._name + '.range[0]'] = ax.range[0]; + // updates[ax._name + '.range[1]'] = ax.range[1]; + // For plotly_relayout, the event is emitted at the end of zoomDone. The payload is not + // computed until zoomAxRanges is called. + // Actual drawing is spread out in several functions + // updateSubplots + // ticksAndAnnotations + // relayout() in plot_api.js + // zoom + // zoomMove + // no drawing + // zoomDone + // zoomAxRanges + // no drawing but modifying updates + // dragtail + // updateSubplots + // drawing subplots + // relayout() in plot_api.js + // zoomwheel + // zoomwheel + // updateSubplots + // ticksandannotations + // this modifies the updates + // dragtail on delay + // updateSubplots + // relayout() in plot_api.js + // + computeZoomUpdates(); + gd.emit('plotly_relayouting', updates); dimmed = true; } @@ -358,7 +390,13 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(Math.min(box.h, box.w) < MINDRAG * 2) { return removeZoombox(gd); } + computeZoomUpdates(); + removeZoombox(gd); + dragTail(); + showDoubleClickNotifier(gd); + } + function computeZoomUpdates() { // TODO: edit linked axes in zoomAxRanges and in dragTail if(zoomMode === 'xy' || zoomMode === 'x') { zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes); @@ -366,10 +404,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(zoomMode === 'xy' || zoomMode === 'y') { zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes); } - - removeZoombox(gd); - dragTail(); - showDoubleClickNotifier(gd); } // scroll zoom, on all draggers except corners @@ -378,7 +412,6 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { var redrawTimer = null; var REDRAWDELAY = constants.REDRAWDELAY; var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo; - function zoomWheel(e) { // deactivate mousewheel scrolling on embedded graphs // devs can override this with layout._enablescrollzoom, @@ -489,6 +522,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { if(yActive) dragAxList(yaxes, dy); updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]); ticksAndAnnotations(yActive, xActive); + // updates computed in ticksAndAnnotations + gd.emit('plotly_relayout', updates); return; } @@ -561,6 +596,8 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateSubplots([x0, y0, pw - dx, ph - dy]); ticksAndAnnotations(yActive, xActive); + // updates computed in ticksAndAnnotations + gd.emit('plotly_relayout', updates); } // Draw ticks and annotations (and other components) when ranges change. From d88f24e17ad724168c6c4f33bed95b258f03004d Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Mon, 28 May 2018 13:39:57 -0400 Subject: [PATCH 09/13] Emit plotly_relayout on mousemove for gl3d. --- src/plots/gl3d/scene.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index af02713a09c..953e5116db3 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -216,7 +216,7 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { scene.graphDiv.emit('plotly_relayout', update); }; - scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene)); + scene.glplot.canvas.addEventListener('mousemove', relayoutCallback.bind(null, scene)); scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false); if(!scene.staticMode) { From 98f28a242d31888f661e5d6a31d1872c4dea19e1 Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Mon, 28 May 2018 13:47:58 -0400 Subject: [PATCH 10/13] Adding relayouting event for polar plots. --- src/plots/polar/polar.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js index 5b8264194a3..89f1f89b940 100644 --- a/src/plots/polar/polar.js +++ b/src/plots/polar/polar.js @@ -680,24 +680,30 @@ proto.updateMainDrag = function(fullLayout, polarLayout) { zb.attr('d', path1); corners.attr('d', cpath); dragBox.transitionZoombox(zb, corners, dimmed, lum); + var updateObj = {}; + computeZoomUpdates(updateObj); + gd.emit('plotly_relayouting', updateObj); dimmed = true; } - function zoomDone() { - dragBox.removeZoombox(gd); - - if(r0 === null || r1 === null) return; - - dragBox.showDoubleClickNotifier(gd); - + function computeZoomUpdates(update) { var radialAxis = _this.radialAxis; var radialRange = radialAxis.range; var drange = radialRange[1] - radialRange[0]; - var updateObj = {}; - updateObj[_this.id + '.radialaxis.range'] = [ + update[_this.id + '.radialaxis.range'] = [ radialRange[0] + r0 * drange / radius, radialRange[0] + r1 * drange / radius ]; + } + + function zoomDone() { + dragBox.removeZoombox(gd); + + if(r0 === null || r1 === null) return; + var updateObj = {}; + computeZoomUpdates(updateObj); + + dragBox.showDoubleClickNotifier(gd); Registry.call('relayout', gd, updateObj); } From 13c69fc898fb7ceeee7bfec04985812f2a4a81a4 Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Mon, 28 May 2018 17:18:15 -0400 Subject: [PATCH 11/13] Add relayouting events for geo, mapbox and ternary plots. --- src/plots/geo/zoom.js | 3 ++- src/plots/mapbox/mapbox.js | 6 ++++++ src/plots/ternary/ternary.js | 24 +++++++++++++++++------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js index ac5d234e9b1..b4212d03df5 100644 --- a/src/plots/geo/zoom.js +++ b/src/plots/geo/zoom.js @@ -80,6 +80,7 @@ function zoomScoped(geo, projection) { .scale(d3.event.scale) .translate(d3.event.translate); geo.render(); + geo.graphDiv.emit('plotly_relayouting', {'projection.scale': projection.scale() / geo.fitScale}); } function syncCb(set) { @@ -153,8 +154,8 @@ function zoomNonClipped(geo, projection) { } geo.render(); + geo.graphDiv.emit('plotly_relayouting', {'projection.scale': projection.scale() / geo.fitScale}); } - function handleZoomend() { d3.select(this).style(zoomendStyle); sync(geo, projection, syncCb); diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 18db3935a4d..0bbb20a735f 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -169,6 +169,12 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { self.yaxis.p2c = function() { return evt.lngLat.lat; }; Fx.hover(gd, evt, self.id); + + var update = {}; + var view = self.getView(); + update[self.id] = Lib.extendFlat({}, view); + gd.emit('plotly_relayouting', update); + }); map.on('click', function(evt) { diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 436397bc5aa..80d83258924 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -587,17 +587,22 @@ proto.initInteractions = function() { .duration(200); dimmed = true; } + var updates = {}; + computeZoomUpdates(updates); + gd.emit('plotly_relayouting', updates); } + function computeZoomUpdates(attrs) { + attrs[_this.id + '.aaxis.min'] = mins.a; + attrs[_this.id + '.baxis.min'] = mins.b; + attrs[_this.id + '.caxis.min'] = mins.c; + } function zoomDone() { removeZoombox(gd); if(mins === mins0) return; - var attrs = {}; - attrs[_this.id + '.aaxis.min'] = mins.a; - attrs[_this.id + '.baxis.min'] = mins.b; - attrs[_this.id + '.caxis.min'] = mins.c; + computeZoomUpdates(attrs); Registry.call('relayout', gd, attrs); @@ -670,14 +675,19 @@ proto.initInteractions = function() { .select('.scatterlayer').selectAll('.trace') .call(Drawing.hideOutsideRangePoints, _this); } - } - - function dragDone() { var attrs = {}; + computeDragUpdates(attrs); + gd.emit('plotly_relayout', attrs); + } + function computeDragUpdates(attrs) { attrs[_this.id + '.aaxis.min'] = mins.a; attrs[_this.id + '.baxis.min'] = mins.b; attrs[_this.id + '.caxis.min'] = mins.c; + } + function dragDone() { + var attrs = {}; + computeDragUpdates(attrs) Registry.call('relayout', gd, attrs); } From a4c7ab0bb7da7d700d6128acf6c00c56a7e51459 Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Mon, 28 May 2018 18:11:18 -0400 Subject: [PATCH 12/13] fix semicolon --- src/plots/ternary/ternary.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 80d83258924..627abbbc0c1 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -687,7 +687,7 @@ proto.initInteractions = function() { function dragDone() { var attrs = {}; - computeDragUpdates(attrs) + computeDragUpdates(attrs); Registry.call('relayout', gd, attrs); } From 2f0d6332aadba4a4f72b301fbd6a5e0fe825a859 Mon Sep 17 00:00:00 2001 From: "Chen, Zhihua" <13683070+zhihua-chen@users.noreply.github.com> Date: Tue, 29 May 2018 15:15:39 -0400 Subject: [PATCH 13/13] Switching to relayouting for plotdrag and gl3d rotations. --- src/plots/cartesian/dragbox.js | 4 ++-- src/plots/gl3d/scene.js | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 792a779baef..8ee86a6da09 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -527,7 +527,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, pw, ph]); ticksAndAnnotations(yActive, xActive); // updates computed in ticksAndAnnotations - gd.emit('plotly_relayout', updates); + gd.emit('plotly_relayouting', updates); return; } @@ -601,7 +601,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { updateSubplots([x0, y0, pw - dx, ph - dy]); ticksAndAnnotations(yActive, xActive); // updates computed in ticksAndAnnotations - gd.emit('plotly_relayout', updates); + gd.emit('plotly_relayouting', updates); } // Draw ticks and annotations (and other components) when ranges change. diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index b793339929a..a5a5f4954cc 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -215,7 +215,16 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { scene.saveCamera(scene.graphDiv.layout); scene.graphDiv.emit('plotly_relayout', update); }; - scene.glplot.canvas.addEventListener('mousemove', relayoutCallback.bind(null, scene)); + var relayoutingCallback = function(scene) { + if(scene.fullSceneLayout.dragmode === false) return; + var update = {}; + update[scene.id + '.camera'] = getLayoutCamera(scene.camera); + // scene.saveCamera(scene.graphDiv.layout); + scene.graphDiv.emit('plotly_relayouting', update); + }; + + scene.glplot.canvas.addEventListener('mousemove', relayoutingCallback.bind(null, scene)); + scene.glplot.canvas.addEventListener('mouseup', relayoutCallback.bind(null, scene)); scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false); if(!scene.staticMode) {