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 3832f922009..69e1e2652c9 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) @@ -310,7 +312,6 @@ proto.cameraChanged = function() { }; proto.destroy = function() { - var traces = this.traces; if(traces) { @@ -334,10 +335,9 @@ proto.plot = function(fullData, calcData, fullLayout) { var glplot = this.glplot, pixelRatio = this.pixelRatio; - var i, j, trace; - this.fullLayout = fullLayout; this.updateAxes(fullLayout); + this.updateTraces(fullData, calcData); var width = fullLayout.width, height = fullLayout.height, @@ -351,34 +351,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]; @@ -404,16 +376,18 @@ 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); + var ax, i; + 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]); } } - var ax; for(i = 0; i < 2; ++i) { if(bounds[i] > bounds[i + 2]) { bounds[i] = -1; @@ -439,6 +413,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 aa9408672f0..4d2061aa50a 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'); @@ -19,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'; @@ -96,7 +98,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 +119,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/heatmap/calc.js b/src/traces/heatmap/calc.js index 72a0dfb2033..ecbb73de28a 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); + // handled in gl2d convert step + if(!isGL2D) { + Axes.expand(xa, xArray); + Axes.expand(ya, yArray); + } var cd0 = {x: xArray, y: yArray, z: z}; diff --git a/src/traces/heatmapgl/convert.js b/src/traces/heatmapgl/convert.js index 1509f916d51..714073c7aae 100644 --- a/src/traces/heatmapgl/convert.js +++ b/src/traces/heatmapgl/convert.js @@ -10,13 +10,14 @@ 'use strict'; var createHeatmap2D = require('gl-heatmap2d'); - +var Axes = require('../../plots/cartesian/axes'); var str2RGBArray = require('../../lib/str2rgbarray'); function Heatmap(scene, uid) { this.scene = scene; this.uid = uid; + this.type = 'heatmapgl'; this.name = ''; this.hoverinfo = 'all'; @@ -87,6 +88,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() { 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 = []; 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);