From c52fbe75379073a9bc69292f18088e73e4e5f977 Mon Sep 17 00:00:00 2001 From: etienne Date: Mon, 28 May 2018 18:07:40 -0400 Subject: [PATCH 1/6] fix #2671 - fix transform on 'scattergl' traces - by mapping 'selectedpoints' input their post-transform values, using trace._indexToPoints - by clearing 'dirty' flag after 1st of 2 _module.calc call, so that 2nd _module.calc does not append phony batches to scene render queue. --- src/lib/index.js | 43 +++++++++++++++++++++++++++++------ src/plots/plots.js | 10 +++++++- src/traces/scattergl/index.js | 5 ++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/lib/index.js b/src/lib/index.js index a6086e300ce..8b9d36d1e11 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -478,6 +478,17 @@ lib.extractOption = function(calcPt, trace, calcKey, traceKey) { if(!Array.isArray(traceVal)) return traceVal; }; +function makePtIndex2PtNumber(indexToPoints) { + var ptIndex2ptNumber = {}; + for(var k in indexToPoints) { + var pts = indexToPoints[k]; + for(var j = 0; j < pts.length; j++) { + ptIndex2ptNumber[pts[j]] = +k; + } + } + return ptIndex2ptNumber; +} + /** Tag selected calcdata items * * N.B. note that point 'index' corresponds to input data array index @@ -498,13 +509,7 @@ lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) { // make pt index-to-number map object, which takes care of transformed traces if(indexToPoints) { - ptIndex2ptNumber = {}; - for(var k in indexToPoints) { - var pts = indexToPoints[k]; - for(var j = 0; j < pts.length; j++) { - ptIndex2ptNumber[pts[j]] = k; - } - } + ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints); } function isCdIndexValid(v) { @@ -525,6 +530,30 @@ lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) { } }; +lib.selIndices2selPoints = function(trace) { + var selectedpoints = trace.selectedpoints; + var indexToPoints = trace._indexToPoints; + + if(indexToPoints) { + var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints); + var out = []; + + for(var i = 0; i < selectedpoints.length; i++) { + var ptIndex = selectedpoints[i]; + if(lib.isIndex(ptIndex)) { + var ptNumber = ptIndex2ptNumber[ptIndex]; + if(lib.isIndex(ptNumber)) { + out.push(ptNumber); + } + } + } + + return out; + } else { + return selectedpoints; + } +}; + /** Returns target as set by 'target' transform attribute * * @param {object} trace : full trace object diff --git a/src/plots/plots.js b/src/plots/plots.js index 5be17ecdfbc..c49f5cb3ae7 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2342,7 +2342,15 @@ plots.doCalcdata = function(gd, traces) { // we need one round of trace module calc before // the calc transform to 'fill in' the categories list // used for example in the data-to-coordinate method - if(_module && _module.calc) _module.calc(gd, trace); + if(_module && _module.calc) { + var cdi = _module.calc(gd, trace); + + // must clear scene 'batches', so that 2nd + // _module.calc call starts from scratch + if(cdi[0] && cdi[0].t && cdi[0].t._scene) { + delete cdi[0].t._scene.dirty; + } + } for(j = 0; j < trace.transforms.length; j++) { var transform = trace.transforms[j]; diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index 934b60c9ca8..17b2096e37c 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -492,12 +492,11 @@ function plot(gd, subplot, cdata) { // regenerate scene batch, if traces number changed during selection if(trace.selectedpoints) { - scene.selectBatch[id] = trace.selectedpoints; + var selPts = scene.selectBatch[id] = Lib.selIndices2selPoints(trace); - var selPts = trace.selectedpoints; var selDict = {}; for(i = 0; i < selPts.length; i++) { - selDict[selPts[i]] = true; + selDict[selPts[i]] = 1; } var unselPts = []; for(i = 0; i < stash.count; i++) { From 9055d49c2932adb7938f0fde1a34e92657c7af04 Mon Sep 17 00:00:00 2001 From: etienne Date: Mon, 28 May 2018 18:08:13 -0400 Subject: [PATCH 2/6] do not scale scattergl line.width by marker.sizeref --- src/traces/scattergl/convert.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 83788abe95a..b165484ef4b 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -192,10 +192,10 @@ function convertMarkerStyle(trace) { // See https://github.com/plotly/plotly.js/pull/1781#discussion_r121820798 if(multiLineWidth) { for(i = 0; i < count; i++) { - borderSizes[i] = markerSizeFunc(optsIn.line.width[i]); + borderSizes[i] = optsIn.line.width[i] / 2; } } else { - s = markerSizeFunc(optsIn.line.width); + s = optsIn.line.width / 2; for(i = 0; i < count; i++) { borderSizes[i] = s; } From 187a1552d8baeb03dd951184b1daf7ba449c9413 Mon Sep 17 00:00:00 2001 From: etienne Date: Mon, 28 May 2018 18:08:51 -0400 Subject: [PATCH 3/6] make scattergl [un]selected.marker.size scale properly --- src/traces/scattergl/convert.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index b165484ef4b..ba3878f1833 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -219,7 +219,7 @@ function convertMarkerSelection(trace, target) { if(target.marker && target.marker.symbol) { optsOut = convertMarkerStyle(Lib.extendFlat({}, optsIn, target.marker)); } else if(target.marker) { - if(target.marker.size) optsOut.sizes = target.marker.size; + if(target.marker.size) optsOut.sizes = target.marker.size / 2; if(target.marker.color) optsOut.colors = target.marker.color; if(target.marker.opacity !== undefined) optsOut.opacity = target.marker.opacity; } From 56c80589f2feee885f014b5b8809bbd9ba2e57ed Mon Sep 17 00:00:00 2001 From: etienne Date: Mon, 28 May 2018 18:10:49 -0400 Subject: [PATCH 4/6] add transform and point-selection mocks of scattergl traces --- test/image/baselines/gl2d_point-selection.png | Bin 0 -> 21822 bytes test/image/baselines/gl2d_transforms.png | Bin 0 -> 28178 bytes test/image/mocks/gl2d_point-selection.json | 74 +++++++++++++++++ test/image/mocks/gl2d_transforms.json | 75 ++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 test/image/baselines/gl2d_point-selection.png create mode 100644 test/image/baselines/gl2d_transforms.png create mode 100644 test/image/mocks/gl2d_point-selection.json create mode 100644 test/image/mocks/gl2d_transforms.json diff --git a/test/image/baselines/gl2d_point-selection.png b/test/image/baselines/gl2d_point-selection.png new file mode 100644 index 0000000000000000000000000000000000000000..f4e67323d98697fc4763fad97a218d27056dcf8e GIT binary patch literal 21822 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 z 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 literal 0 HcmV?d00001 diff --git a/test/image/mocks/gl2d_point-selection.json b/test/image/mocks/gl2d_point-selection.json new file mode 100644 index 00000000000..a7427865108 --- /dev/null +++ b/test/image/mocks/gl2d_point-selection.json @@ -0,0 +1,74 @@ +{ + "data": [{ + "type": "scattergl", + "mode": "lines+markers+text", + "x": [1, 2, 3, 4, 5, 6], + "y": [1, 3, 2, 4, 5, 7], + "ids": ["a", "b", "c", "d", "e", "f"], + "text": ["a", "b", "c", "d", "e", "f"], + "marker": { + "color": "#67353E", + "size": 12 + }, + "textposition": "top left", + "selectedpoints": [1, 2, 3], + "selected": { + "marker": { + "color": "blue", + "size": 20 + } + }, + "unselected": { + "marker": { + "color": "green", + "opacity": 0.5 + } + }, + "transforms": [{ + "type": "filter", + "target": "x", + "operation": "][", + "value": [3.5, 4.5] + }] + }, { + "type": "scattergl", + "mode": "lines+markers+text", + "x": [1, 2, 3, 4, 5, 6], + "y": [6, 5, 6, 5, 4, 3], + "ids": ["a", "b", "c", "d", "e", "f"], + "text": ["a", "b", "c", "d", "e", "f"], + "marker": { + "color": "#34ABA2", + "size": 12 + }, + "textposition": "top left", + "selectedpoints": [], + "selected": { + "marker": { + "color": "green" + } + }, + "unselected": { + "marker": { + "color": "blue", + "opacity": 0.5 + } + }, + "transforms": [{ + "type": "filter", + "target": "x", + "operation": "][", + "value": [3.5, 4.5] + }] + }], + "layout": { + "dragmode": "lasso", + "legend": { + "x": 0, + "y": 1, + "xanchor": "left", + "yanchor": "bottom" + }, + "width": 600 + } +} diff --git a/test/image/mocks/gl2d_transforms.json b/test/image/mocks/gl2d_transforms.json new file mode 100644 index 00000000000..ce6a128ae92 --- /dev/null +++ b/test/image/mocks/gl2d_transforms.json @@ -0,0 +1,75 @@ +{ + "data": [{ + "type": "scattergl", + "mode": "lines+markers", + "x": [1, -1, -2, 0, 1, 3, 3], + "y": [2, 1, 0, 1, 3, 4, 3], + "transforms": [{ + "type": "groupby", + "groups": ["a", "a", "b", "a", "b", "b", "a"], + "styles": [ + {"target": "a", "value": {"marker": {"color": "orange"}}}, + {"target": "b", "value": {"marker": {"color": "blue"}}} + ] + }, { + "type": "filter", + "target": "x", + "operation": ">=", + "value": 0, + "preservegaps": true + }], + "name": "Groupby+filter" + }, + { + "type": "scattergl", + "x": [1, 2, 3, 4, -3], + "y": [1.1, 2.2, 3.3, 4.4, 5.5], + "marker": { + "size": [0.3, 0.2, 0.1, 0.4, 0.5], + "sizeref": 0.01, + "color": [2, 4, 6, 10, 8], + "opacity": [0.9, 0.6, 0.2, 0.8, 1.0], + "line": { + "color": [2.2, 3.3, 4.4, 5.5, 1.1] + } + }, + "transforms": [{ + "type": "aggregate", + "groups": ["a", "b", "a", "a", "a"], + "aggregations": [ + {"target": "x", "func": "sum"}, + {"target": "y", "func": "avg"}, + {"target": "marker.size", "func": "min"}, + {"target": "marker.color", "func": "max"}, + {"target": "marker.line.color", "func": "last"}, + {"target": "marker.line.width", "func": "count"} + ] + }], + "name": "Aggregate" + }, + { + "type": "scattergl", + "x": [1, 2, 3, 4, 5, 6], + "y": [1, 4, 2, 6, 5, 3], + "transforms": [{ + "type": "sort", + "target": [1, 6, 2, 5, 3, 4] + }], + "name": "Sort" + }, + { + "type": "scattergl", + "x":[4, 5, 6, 4, 5, 6], + "y": [1, 1, 1, 2, 2, 2], + "marker": {"color": [1, 2, 3, -1, -2, -3], "size": 20}, + "mode": "lines+markers", + "transforms": [ + {"type": "groupby", "groups": [1, 1, 1, 2, 2, 2]} + ] + }], + "layout": { + "width": 600, + "height": 400, + "title": "Transforms on scattergl traces" + } +} From b85ffe22a722a3325e95b6c71a2038a554e92379 Mon Sep 17 00:00:00 2001 From: etienne Date: Tue, 29 May 2018 09:49:59 -0400 Subject: [PATCH 5/6] sub fail -> failTest --- test/jasmine/tests/gl2d_click_test.js | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 3a6f5635fa8..bef3d1a3a1e 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -4,7 +4,7 @@ var Lib = require('@src/lib'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); -var fail = require('../assets/fail_test.js'); +var failTest = require('../assets/fail_test.js'); var customAssertions = require('../assets/custom_assertions'); var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle; @@ -217,7 +217,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -256,7 +256,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -295,7 +295,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -315,7 +315,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -343,7 +343,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -376,7 +376,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -409,7 +409,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); @@ -451,7 +451,7 @@ describe('@gl @flaky Test hover and click interactions', function() { return Plotly.restyle(gd, 'visible', false, [1]); }) .then(run2) - .catch(fail) + .catch(failTest) .then(done); }); @@ -499,7 +499,7 @@ describe('@gl @flaky Test hover and click interactions', function() { return Plotly.restyle(gd, 'visible', false, [1]); }) .then(run2) - .catch(fail) + .catch(failTest) .then(done); }); @@ -527,7 +527,7 @@ describe('@gl @flaky Test hover and click interactions', function() { Plotly.plot(gd, _mock) .then(run) - .catch(fail) + .catch(failTest) .then(done); }); }); @@ -623,7 +623,7 @@ describe('@noCI @gl Test gl2d lasso/select:', function() { }); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -647,7 +647,7 @@ describe('@noCI @gl Test gl2d lasso/select:', function() { ] }); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -667,7 +667,7 @@ describe('@noCI @gl Test gl2d lasso/select:', function() { points: [{x: 0.004, y: 12.5}] }); }) - .catch(fail) + .catch(failTest) .then(done); }); @@ -686,7 +686,7 @@ describe('@noCI @gl Test gl2d lasso/select:', function() { points: [{ x: 0.099, y: 2.75 }] }); }) - .catch(fail) + .catch(failTest) .then(done); }); }); From 86d6a9598efb2aca1abf69b02002a271eed176ce Mon Sep 17 00:00:00 2001 From: etienne Date: Tue, 29 May 2018 10:32:56 -0400 Subject: [PATCH 6/6] :lock: scattergl transform behavior --- test/jasmine/tests/gl2d_click_test.js | 23 +++++++ test/jasmine/tests/gl2d_plot_interact_test.js | 66 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index bef3d1a3a1e..1bade67fc6a 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -689,4 +689,27 @@ describe('@noCI @gl Test gl2d lasso/select:', function() { .catch(failTest) .then(done); }); + + it('should work on trace with enabled transforms', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/gl2d_transforms.json')); + fig.layout.dragmode = 'select'; + fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; + fig.layout.height = 500; + fig.layout.width = 500; + gd = createGraphDiv(); + + Plotly.plot(gd, fig) + .then(delay(100)) + .then(function() { return select([[100, 100], [250, 250]]); }) + .then(function(eventData) { + assertEventData(eventData, { + points: [ + { x: 3, y: 4 }, + { x: 2, y: 4 } + ] + }); + }) + .catch(failTest) + .then(done); + }); }); diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 15f5c92169d..f2be65b1fa9 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -962,6 +962,72 @@ describe('@gl Test gl2d plots', function() { .catch(failTest) .then(done); }); + + it('should handle transform traces properly (calcTransform case)', function(done) { + spyOn(ScatterGl, 'calc').and.callThrough(); + + Plotly.plot(gd, [{ + type: 'scattergl', + x: [1, 2, 3], + y: [1, 2, 1], + transforms: [{ + type: 'filter', + target: 'x', + operation: '>', + value: 1 + }] + }]) + .then(function() { + expect(ScatterGl.calc).toHaveBeenCalledTimes(2); + + var opts = gd.calcdata[0][0].t._scene.markerOptions; + // length === 2 before #2677 + expect(opts.length).toBe(1); + + return Plotly.restyle(gd, 'selectedpoints', [[1]]); + }) + .then(function() { + // was === 1 before #2677 + var scene = gd.calcdata[0][0].t._scene; + expect(scene.selectBatch[0]).toEqual([0]); + }) + .catch(failTest) + .then(done); + }); + + it('should handle transform traces properly (default transform case)', function(done) { + spyOn(ScatterGl, 'calc').and.callThrough(); + + Plotly.plot(gd, [{ + type: 'scattergl', + x: [1, 2, 3], + y: [1, 2, 1], + transforms: [{ + type: 'groupby', + groups: ['a', 'b', 'a'] + }] + }]) + .then(function() { + // twice per 'expanded' trace + expect(ScatterGl.calc).toHaveBeenCalledTimes(4); + + // 'scene' from opts0 and opts1 is linked to the same object, + // which has two items, one for each 'expanded' trace + var opts0 = gd.calcdata[0][0].t._scene.markerOptions; + expect(opts0.length).toBe(2); + + var opts1 = gd.calcdata[1][0].t._scene.markerOptions; + expect(opts1.length).toBe(2); + + return Plotly.restyle(gd, 'selectedpoints', [[1]]); + }) + .then(function() { + var scene = gd.calcdata[0][0].t._scene; + expect(scene.selectBatch).toEqual([[], [0]]); + }) + .catch(failTest) + .then(done); + }); }); describe('Test scattergl autorange:', function() {