From 330c5f3cd1aaccad9fa1c9607006f5891dd78c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 15 Aug 2016 13:34:30 -0400 Subject: [PATCH 1/6] expand axes within convert step - to ensure that the reset-axes and autoscale mode bar buttons works as desired. --- src/traces/contourgl/convert.js | 6 +++++- src/traces/heatmapgl/convert.js | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/traces/contourgl/convert.js b/src/traces/contourgl/convert.js index aa9408672f0..3a4b0abb3e8 100644 --- a/src/traces/contourgl/convert.js +++ b/src/traces/contourgl/convert.js @@ -12,6 +12,7 @@ var createContour2D = require('gl-contour2d'); var createHeatmap2D = require('gl-heatmap2d'); +var Axes = require('../../plots/cartesian/axes'); var makeColorMap = require('../contour/make_color_map'); var str2RGBArray = require('../../lib/str2rgbarray'); @@ -96,7 +97,6 @@ proto.update = function(fullTrace, calcTrace) { this.contourOptions.x = this.heatmapOptions.x = calcPt.x; this.contourOptions.y = this.heatmapOptions.y = calcPt.y; - // pass on fill information if(fullTrace.contours.coloring === 'fill') { colorOptions = convertColorScale(fullTrace, {fill: true}); @@ -118,6 +118,10 @@ proto.update = function(fullTrace, calcTrace) { this.contour.update(this.contourOptions); this.heatmap.update(this.heatmapOptions); + + // expand axes + Axes.expand(this.scene.xaxis, calcPt.x); + Axes.expand(this.scene.yaxis, calcPt.y); }; proto.dispose = function() { diff --git a/src/traces/heatmapgl/convert.js b/src/traces/heatmapgl/convert.js index 1509f916d51..b2a12d1b704 100644 --- a/src/traces/heatmapgl/convert.js +++ b/src/traces/heatmapgl/convert.js @@ -10,7 +10,7 @@ 'use strict'; var createHeatmap2D = require('gl-heatmap2d'); - +var Axes = require('../../plots/cartesian/axes'); var str2RGBArray = require('../../lib/str2rgbarray'); @@ -87,6 +87,9 @@ proto.update = function(fullTrace, calcTrace) { this.textLabels = [].concat.apply([], fullTrace.text); this.heatmap.update(this.options); + + Axes.expand(this.scene.xaxis, calcPt.x); + Axes.expand(this.scene.yaxis, calcPt.y); }; proto.dispose = function() { From 716f2cde3de0e4de4584e5d7efa155f369b33e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 15 Aug 2016 13:35:33 -0400 Subject: [PATCH 2/6] make gl2d traces skip over Axes.expand step in heatmap calc - as it is now called in the convert step - no duplication --- src/traces/heatmap/calc.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 72a0dfb2033..f589ee38ea7 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -29,6 +29,7 @@ module.exports = function calc(gd, trace) { ya = Axes.getFromId(gd, trace.yaxis || 'y'), isContour = Plots.traceIs(trace, 'contour'), isHist = Plots.traceIs(trace, 'histogram'), + isGL2D = Plots.traceIs(trace, 'gl2d'), zsmooth = isContour ? 'best' : trace.zsmooth, x, x0, @@ -112,8 +113,11 @@ module.exports = function calc(gd, trace) { yIn = trace.ytype === 'scaled' ? '' : trace.y, yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya); - Axes.expand(xa, xArray); - Axes.expand(ya, yArray); + // handler in gl2d convert step + if(!isGL2D) { + Axes.expand(xa, xArray); + Axes.expand(ya, yArray); + } var cd0 = {x: xArray, y: yArray, z: z}; From e0d05f6265c6ccf91d34dc2d1fde3e51abce075e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 15 Aug 2016 18:29:26 -0400 Subject: [PATCH 3/6] gl2d: un-set axis autorange after interactions --- src/plots/gl2d/camera.js | 10 ++++++++-- src/plots/gl2d/scene2d.js | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plots/gl2d/camera.js b/src/plots/gl2d/camera.js index 5ebe9ecc46b..50fd45eaa78 100644 --- a/src/plots/gl2d/camera.js +++ b/src/plots/gl2d/camera.js @@ -32,6 +32,11 @@ function createCamera(scene) { plot = scene.glplot, result = new Camera2D(element, plot); + function unSetAutoRange() { + scene.xaxis.autorange = false; + scene.yaxis.autorange = false; + } + result.mouseListener = mouseChange(element, function(buttons, x, y) { var xrange = scene.xaxis.range, yrange = scene.yaxis.range, @@ -84,7 +89,7 @@ function createCamera(scene) { else if(result.boxEnabled) { updateRange(xrange, result.boxStart[0], result.boxEnd[0]); updateRange(yrange, result.boxStart[1], result.boxEnd[1]); - + unSetAutoRange(); result.boxEnabled = false; } break; @@ -104,7 +109,7 @@ function createCamera(scene) { yrange[1] += dy; result.lastInputTime = Date.now(); - + unSetAutoRange(); scene.cameraChanged(); } break; @@ -142,6 +147,7 @@ function createCamera(scene) { yrange[1] = (yrange[1] - cy) * scale + cy; result.lastInputTime = Date.now(); + unSetAutoRange(); scene.cameraChanged(); break; } diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 2626eb71f48..a5ddba119f0 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -275,7 +275,9 @@ var relayoutCallback = function(scene) { yrange = scene.yaxis.range; // Update the layout on the DIV + scene.graphDiv.layout.xaxis.autorange = scene.xaxis.autorange; scene.graphDiv.layout.xaxis.range = xrange.slice(0); + scene.graphDiv.layout.yaxis.autorange = scene.yaxis.autorange; scene.graphDiv.layout.yaxis.range = yrange.slice(0); // Make a meaningful value to be passed on to the possible 'plotly_relayout' subscriber(s) From 51cec035e2ed16abb0abdeff012a1797b808d88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 15 Aug 2016 18:30:03 -0400 Subject: [PATCH 4/6] gl2d: clean up update trace step --- src/plots/gl2d/scene2d.js | 78 +++++++++++++++++++-------------- src/traces/contourgl/convert.js | 1 + src/traces/heatmapgl/convert.js | 1 + src/traces/scattergl/convert.js | 1 + 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index a5ddba119f0..37abcb46020 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -312,7 +312,6 @@ proto.cameraChanged = function() { }; proto.destroy = function() { - var traces = this.traces; if(traces) { @@ -336,10 +335,11 @@ proto.plot = function(fullData, calcData, fullLayout) { var glplot = this.glplot, pixelRatio = this.pixelRatio; - var i, j, trace; + var i; this.fullLayout = fullLayout; this.updateAxes(fullLayout); + this.updateTraces(fullData, calcData); var width = fullLayout.width, height = fullLayout.height, @@ -353,34 +353,6 @@ proto.plot = function(fullData, calcData, fullLayout) { canvas.height = pixelHeight; } - // update traces - for(i = 0; i < fullData.length; ++i) { - var fullTrace = fullData[i], - calcTrace = calcData[i]; - trace = this.traces[fullTrace.uid]; - - if(trace) trace.update(fullTrace, calcTrace); - else { - trace = fullTrace._module.plot(this, fullTrace, calcTrace); - } - - this.traces[fullTrace.uid] = trace; - } - - // remove empty traces - var traceIds = Object.keys(this.traces); - - trace_id_loop: - for(i = 0; i < traceIds.length; ++i) { - for(j = 0; j < calcData.length; ++j) { - if(calcData[j][0].trace.uid === traceIds[i]) continue trace_id_loop; - } - - trace = this.traces[traceIds[i]]; - trace.dispose(); - delete this.traces[traceIds[i]]; - } - var options = this.glplotOptions; options.merge(fullLayout); options.screenBox = [0, 0, width, height]; @@ -406,12 +378,13 @@ proto.plot = function(fullData, calcData, fullLayout) { bounds[0] = bounds[1] = Infinity; bounds[2] = bounds[3] = -Infinity; - traceIds = Object.keys(this.traces); + var traceIds = Object.keys(this.traces); for(i = 0; i < traceIds.length; ++i) { - trace = this.traces[traceIds[i]]; + var traceObj = this.traces[traceIds[i]]; + for(var k = 0; k < 2; ++k) { - bounds[k] = Math.min(bounds[k], trace.bounds[k]); - bounds[k + 2] = Math.max(bounds[k + 2], trace.bounds[k + 2]); + bounds[k] = Math.min(bounds[k], traceObj.bounds[k]); + bounds[k + 2] = Math.max(bounds[k + 2], traceObj.bounds[k + 2]); } } @@ -441,6 +414,43 @@ proto.plot = function(fullData, calcData, fullLayout) { this.glplot.draw(); }; +proto.updateTraces = function(fullData, calcData) { + var traceIds = Object.keys(this.traces); + var i, j, fullTrace; + + // remove empty traces + trace_id_loop: + for(i = 0; i < traceIds.length; i++) { + var oldUid = traceIds[i], + oldTrace = this.traces[oldUid]; + + for(j = 0; j < fullData.length; j++) { + fullTrace = fullData[j]; + + if(fullTrace.uid === oldUid && fullTrace.type === oldTrace.type) { + continue trace_id_loop; + } + } + + oldTrace.dispose(); + delete this.traces[oldUid]; + } + + // update / create trace objects + for(i = 0; i < fullData.length; i++) { + fullTrace = fullData[i]; + + var calcTrace = calcData[i], + traceObj = this.traces[fullTrace.uid]; + + if(traceObj) traceObj.update(fullTrace, calcTrace); + else { + traceObj = fullTrace._module.plot(this, fullTrace, calcTrace); + this.traces[fullTrace.uid] = traceObj; + } + } +}; + proto.draw = function() { if(this.stopped) return; diff --git a/src/traces/contourgl/convert.js b/src/traces/contourgl/convert.js index 3a4b0abb3e8..4d2061aa50a 100644 --- a/src/traces/contourgl/convert.js +++ b/src/traces/contourgl/convert.js @@ -20,6 +20,7 @@ var str2RGBArray = require('../../lib/str2rgbarray'); function Contour(scene, uid) { this.scene = scene; this.uid = uid; + this.type = 'contourgl'; this.name = ''; this.hoverinfo = 'all'; diff --git a/src/traces/heatmapgl/convert.js b/src/traces/heatmapgl/convert.js index b2a12d1b704..714073c7aae 100644 --- a/src/traces/heatmapgl/convert.js +++ b/src/traces/heatmapgl/convert.js @@ -17,6 +17,7 @@ var str2RGBArray = require('../../lib/str2rgbarray'); function Heatmap(scene, uid) { this.scene = scene; this.uid = uid; + this.type = 'heatmapgl'; this.name = ''; this.hoverinfo = 'all'; diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index b3b544e1727..5dbc421870e 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -32,6 +32,7 @@ var AXES = ['xaxis', 'yaxis']; function LineWithMarkers(scene, uid) { this.scene = scene; this.uid = uid; + this.type = 'scattergl'; this.pickXData = []; this.pickYData = []; From 885dae73e56d6cd8f3a0c9f5efbde300e1cd8427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 15 Aug 2016 18:51:33 -0400 Subject: [PATCH 5/6] test: add a few more gl2d interaction tests --- .../tests/gl2d_scatterplot_contour_test.js | 44 ++++++++++++++++++- test/jasmine/tests/gl_plot_interact_test.js | 5 +++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/test/jasmine/tests/gl2d_scatterplot_contour_test.js b/test/jasmine/tests/gl2d_scatterplot_contour_test.js index 9511141c982..92430b62635 100644 --- a/test/jasmine/tests/gl2d_scatterplot_contour_test.js +++ b/test/jasmine/tests/gl2d_scatterplot_contour_test.js @@ -4,8 +4,9 @@ var Plotly = require('@lib/index'); var Lib = require('@src/lib'); var d3 = require('d3'); -// contourgl is not part of the dist plotly.js bundle initially +// heatmapgl & contourgl is not part of the dist plotly.js bundle initially Plotly.register( + require('@lib/heatmapgl'), require('@lib/contourgl') ); @@ -208,4 +209,45 @@ describe('contourgl plots', function() { mock.data[0].line = {smoothing: 0}; makePlot(gd, mock, done); }); + + it('should update properly', function(done) { + var mock = plotDataElliptical(0); + var scene2d; + + Plotly.plot(gd, mock.data, mock.layout).then(function() { + scene2d = gd._fullLayout._plots.xy._scene2d; + + expect(scene2d.traces[mock.data[0].uid].type).toEqual('contourgl'); + expect(scene2d.xaxis._min).toEqual([{ val: -1, pad: 0}]); + expect(scene2d.xaxis._max).toEqual([{ val: 1, pad: 0}]); + + return Plotly.relayout(gd, 'xaxis.range', [0, -10]); + }).then(function() { + expect(scene2d.xaxis._min).toEqual([]); + expect(scene2d.xaxis._max).toEqual([]); + + return Plotly.relayout(gd, 'xaxis.autorange', true); + }).then(function() { + expect(scene2d.xaxis._min).toEqual([{ val: -1, pad: 0}]); + expect(scene2d.xaxis._max).toEqual([{ val: 1, pad: 0}]); + + return Plotly.restyle(gd, 'type', 'heatmapgl'); + }).then(function() { + expect(scene2d.traces[mock.data[0].uid].type).toEqual('heatmapgl'); + expect(scene2d.xaxis._min).toEqual([{ val: -1, pad: 0}]); + expect(scene2d.xaxis._max).toEqual([{ val: 1, pad: 0}]); + + return Plotly.relayout(gd, 'xaxis.range', [0, -10]); + }).then(function() { + expect(scene2d.xaxis._min).toEqual([]); + expect(scene2d.xaxis._max).toEqual([]); + + return Plotly.relayout(gd, 'xaxis.autorange', true); + }).then(function() { + expect(scene2d.xaxis._min).toEqual([{ val: -1, pad: 0}]); + expect(scene2d.xaxis._max).toEqual([{ val: 1, pad: 0}]); + + done(); + }); + }); }); diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index 5fca392c06a..8cbd3b1b5a7 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -248,6 +248,8 @@ describe('Test gl plot interactions', function() { var newX = [-0.23224043715846995, 4.811895754518705]; var newY = [-1.2962655110623016, 4.768255474123081]; + expect(gd.layout.xaxis.autorange).toBe(true); + expect(gd.layout.yaxis.autorange).toBe(true); expect(gd.layout.xaxis.range).toBeCloseToArray(originalX, precision); expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision); @@ -270,6 +272,9 @@ describe('Test gl plot interactions', function() { mouseEvent('mousemove', 220, 200, {buttons: 1}); + expect(gd.layout.xaxis.autorange).toBe(false); + expect(gd.layout.yaxis.autorange).toBe(false); + expect(gd.layout.xaxis.range).toBeCloseToArray(newX, precision); expect(gd.layout.yaxis.range).toBeCloseToArray(originalY, precision); From d918264ac67e3d52295880f059f54ce39c123af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Thu, 18 Aug 2016 13:59:59 -0400 Subject: [PATCH 6/6] bow to the :cow2: --- src/plots/gl2d/scene2d.js | 5 ++--- src/traces/heatmap/calc.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plots/gl2d/scene2d.js b/src/plots/gl2d/scene2d.js index 37abcb46020..f275a2939ff 100644 --- a/src/plots/gl2d/scene2d.js +++ b/src/plots/gl2d/scene2d.js @@ -335,8 +335,6 @@ proto.plot = function(fullData, calcData, fullLayout) { var glplot = this.glplot, pixelRatio = this.pixelRatio; - var i; - this.fullLayout = fullLayout; this.updateAxes(fullLayout); this.updateTraces(fullData, calcData); @@ -379,6 +377,8 @@ proto.plot = function(fullData, calcData, fullLayout) { bounds[2] = bounds[3] = -Infinity; var traceIds = Object.keys(this.traces); + var ax, i; + for(i = 0; i < traceIds.length; ++i) { var traceObj = this.traces[traceIds[i]]; @@ -388,7 +388,6 @@ proto.plot = function(fullData, calcData, fullLayout) { } } - var ax; for(i = 0; i < 2; ++i) { if(bounds[i] > bounds[i + 2]) { bounds[i] = -1; diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index f589ee38ea7..ecbb73de28a 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -113,7 +113,7 @@ module.exports = function calc(gd, trace) { yIn = trace.ytype === 'scaled' ? '' : trace.y, yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya); - // handler in gl2d convert step + // handled in gl2d convert step if(!isGL2D) { Axes.expand(xa, xArray); Axes.expand(ya, yArray);