From d88abf81a9baec14e4d323320855e8f82a6fd192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Feb 2017 17:30:52 -0500 Subject: [PATCH 1/6] fix gl2d annotations test --- test/jasmine/tests/gl_plot_interact_test.js | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index 21db7afabc7..ecc4e37b5a6 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -3,6 +3,7 @@ var d3 = require('d3'); var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); var Lib = require('@src/lib'); +var Drawing = require('@src/components/drawing'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); @@ -587,6 +588,7 @@ describe('Test gl plot interactions', function() { }); }); }); + describe('Plotly.newPlot', function() { var mockData2dNew = [{ @@ -761,20 +763,18 @@ describe('gl2d interaction', function() { it('data-referenced annotations should update on drag', function(done) { function drag(start, end) { - var opts = { buttons: 1 }; - - mouseEvent('mousemove', start[0], start[1], opts); - mouseEvent('mousedown', start[0], start[1], opts); - mouseEvent('mousemove', end[0], end[1], opts); - mouseEvent('mouseup', end[0], end[1], opts); + mouseEvent('mousemove', start[0], start[1]); + mouseEvent('mousedown', start[0], start[1], { buttons: 1 }); + mouseEvent('mousemove', end[0], end[1], { buttons: 1 }); + mouseEvent('mouseup', end[0], end[1]); } function assertAnnotation(xy) { - var ann = d3.select('g.annotation-text-g'); - var x = +ann.attr('x'); - var y = +ann.attr('y'); + var ann = d3.select('g.annotation-text-g').select('g'); + var translate = Drawing.getTranslate(ann); - expect([x, y]).toBeCloseToArray(xy); + expect(translate.x).toBeWithin(xy[0], 1.5); + expect(translate.y).toBeWithin(xy[1], 1.5); } Plotly.plot(gd, [{ @@ -790,10 +790,10 @@ describe('gl2d interaction', function() { dragmode: 'pan' }) .then(function() { - assertAnnotation([340, 334]); + assertAnnotation([327, 325]); - drag([250, 200], [150, 300]); - assertAnnotation([410, 264]); + drag([250, 200], [200, 150]); + assertAnnotation([277, 275]); return Plotly.relayout(gd, { 'xaxis.range': [1.5, 2.5], @@ -801,7 +801,7 @@ describe('gl2d interaction', function() { }); }) .then(function() { - assertAnnotation([340, 340]); + assertAnnotation([327, 331]); }) .then(done); }); From f276f84f40ebfd621277e6bd6216c4a865cbb53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Feb 2017 17:32:41 -0500 Subject: [PATCH 2/6] rm useless logic in gl3d .plot - setting paperdiv style is done in the layoutStyles plot_api subroutine --- src/plots/gl3d/index.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/plots/gl3d/index.js b/src/plots/gl3d/index.js index 5d31b1c7db4..a22a9ca3240 100644 --- a/src/plots/gl3d/index.js +++ b/src/plots/gl3d/index.js @@ -37,21 +37,13 @@ exports.plot = function plotGl3d(gd) { fullData = gd._fullData, sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'); - fullLayout._paperdiv.style({ - width: fullLayout.width + 'px', - height: fullLayout.height + 'px' - }); - - gd._context.setBackground(gd, fullLayout.paper_bgcolor); - for(var i = 0; i < sceneIds.length; i++) { var sceneId = sceneIds[i], fullSceneData = Plots.getSubplotData(fullData, 'gl3d', sceneId), sceneLayout = fullLayout[sceneId], scene = sceneLayout._scene; - // If Scene is not instantiated, create one! - if(scene === undefined) { + if(!scene) { initAxes(gd, sceneLayout); scene = new Scene({ From 99ad3cbd207e0f85319344a224c7c25f9d998f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Feb 2017 17:34:06 -0500 Subject: [PATCH 3/6] add doCamera subroutine - which calls scene.setCamera in relayout/update --- src/plot_api/plot_api.js | 8 +++++++- src/plot_api/subroutines.js | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index e1c31b6f788..3f5f80444d5 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1724,6 +1724,7 @@ Plotly.relayout = function relayout(gd, astr, val) { if(flags.dolayoutstyle) seq.push(subroutines.layoutStyles); if(flags.doticks) seq.push(subroutines.doTicksRelayout); if(flags.domodebar) seq.push(subroutines.doModeBar); + if(flags.docamera) seq.push(subroutines.doCamera); } Queue.add(gd, @@ -1771,6 +1772,7 @@ function _relayout(gd, aobj) { doplot: false, docalc: false, domodebar: false, + docamera: false, layoutReplot: false }; @@ -1967,7 +1969,10 @@ function _relayout(gd, aobj) { var pp1 = String(p.parts[1] || ''); // check whether we can short-circuit a full redraw // 3d or geo at this point just needs to redraw. - if(p.parts[0].indexOf('scene') === 0) flags.doplot = true; + if(p.parts[0].indexOf('scene') === 0) { + if(p.parts[1] === 'camera') flags.docamera = true; + else flags.doplot = true; + } else if(p.parts[0].indexOf('geo') === 0) flags.doplot = true; else if(p.parts[0].indexOf('ternary') === 0) flags.doplot = true; else if(ai === 'paper_bgcolor') flags.doplot = true; @@ -2119,6 +2124,7 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, traces) { if(relayoutFlags.dolayoutstyle) seq.push(subroutines.layoutStyles); if(relayoutFlags.doticks) seq.push(subroutines.doTicksRelayout); if(relayoutFlags.domodebar) seq.push(subroutines.doModeBar); + if(relayoutFlags.doCamera) seq.push(subroutines.doCamera); } Queue.add(gd, diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 67d4e12d270..ceb2a4dbf64 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -314,3 +314,15 @@ exports.doModeBar = function(gd) { return Plots.previousPromises(gd); }; + +exports.doCamera = function(gd) { + var fullLayout = gd._fullLayout, + sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'); + + for(var i = 0; i < sceneIds.length; i++) { + var sceneLayout = fullLayout[sceneIds[i]], + scene = sceneLayout._scene; + + scene.setCamera(sceneLayout.camera); + } +}; From 604f7a0a4b443c15cc6f872122150ad653305083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Feb 2017 17:35:59 -0500 Subject: [PATCH 4/6] refactor modebar gl3d camera handler to use Plotly.relayout - instead of hacky setCamera call - save 'initial' camera setting during scene creation - use that + relayout to set camera to last save - no need to emit plotly_relayout in setCamera no more! --- src/components/modebar/buttons.js | 17 ++++++++++------- src/plots/gl3d/index.js | 6 ++++++ src/plots/gl3d/scene.js | 22 +++------------------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 8e7125825a8..aae0503e368 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -306,20 +306,23 @@ function handleCamera3d(gd, ev) { var button = ev.currentTarget, attr = button.getAttribute('data-attr'), fullLayout = gd._fullLayout, - sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'); + sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d'), + aobj = {}; for(var i = 0; i < sceneIds.length; i++) { var sceneId = sceneIds[i], - fullSceneLayout = fullLayout[sceneId], - scene = fullSceneLayout._scene; + key = sceneId + '.camera', + scene = fullLayout[sceneId]._scene; - if(attr === 'resetDefault') scene.setCameraToDefault(); + if(attr === 'resetDefault') { + aobj[key] = null; + } else if(attr === 'resetLastSave') { - // This handler looks in the un-updated fullLayout.scene.camera object to reset the camera - // to the last saved position. - scene.setCamera(fullSceneLayout.camera); + aobj[key] = Lib.extendDeep({}, scene.cameraInitial); } } + + Plotly.relayout(gd, aobj); } modeBarButtons.hoverClosest3d = { diff --git a/src/plots/gl3d/index.js b/src/plots/gl3d/index.js index a22a9ca3240..6e42343abc0 100644 --- a/src/plots/gl3d/index.js +++ b/src/plots/gl3d/index.js @@ -11,6 +11,7 @@ var Scene = require('./scene'); var Plots = require('../plots'); +var Lib = require('../../lib'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); var axesNames = ['xaxis', 'yaxis', 'zaxis']; @@ -60,6 +61,11 @@ exports.plot = function plotGl3d(gd) { sceneLayout._scene = scene; } + // save 'initial' camera settings for modebar button + if(!scene.cameraInitial) { + scene.cameraInitial = Lib.extendDeep({}, sceneLayout.camera); + } + scene.plot(fullSceneData, fullLayout, gd.layout); } }; diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 25b42cd4698..f287a5c3aa9 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -321,6 +321,7 @@ function computeTraceBounds(scene, trace, bounds) { } proto.plot = function(sceneData, fullLayout, layout) { + // Save parameters this.plotArgs = [sceneData, fullLayout, layout]; @@ -343,7 +344,8 @@ proto.plot = function(sceneData, fullLayout, layout) { this.axesOptions.merge(fullSceneLayout); this.spikeOptions.merge(fullSceneLayout); - // Update camera mode + // Update camera and camera mode + this.setCamera(fullSceneLayout.camera); this.updateFx(fullSceneLayout.dragmode, fullSceneLayout.hovermode); // Update scene @@ -564,18 +566,6 @@ proto.destroy = function() { this.glplot = null; }; - -// for reset camera button in mode bar -proto.setCameraToDefault = function setCameraToDefault() { - // as in Gl3d.layoutAttributes - - this.setCamera({ - eye: { x: 1.25, y: 1.25, z: 1.25 }, - center: { x: 0, y: 0, z: 0 }, - up: { x: 0, y: 0, z: 1 } - }); -}; - // getOrbitCamera :: plotly_coords -> orbit_camera_coords // inverse of getLayoutCamera function getOrbitCamera(camera) { @@ -604,13 +594,7 @@ proto.getCamera = function getCamera() { // set camera position with a set of plotly coords proto.setCamera = function setCamera(cameraData) { - - var update = {}; - - update[this.id] = cameraData; - this.glplot.camera.lookAt.apply(this, getOrbitCamera(cameraData)); - this.graphDiv.emit('plotly_relayout', update); }; // save camera to user layout (i.e. gd.layout) From 66d749ac92ba81282d6dce7e3f7da1952ebfbf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 6 Feb 2017 21:22:07 -0500 Subject: [PATCH 5/6] flatten gl3d modebar test --- test/jasmine/tests/gl_plot_interact_test.js | 161 ++++++++++---------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index ecc4e37b5a6..25779564edc 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -404,95 +404,90 @@ describe('Test gl plot interactions', function() { describe('modebar click handlers', function() { - describe('button zoom3d', function() { - it('should updates the scene dragmode and dragmode button', function() { - var buttonTurntable = selectButton(modeBar, 'tableRotation'), - buttonZoom3d = selectButton(modeBar, 'zoom3d'); - - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonZoom3d.isActive()).toBe(false); - - buttonZoom3d.click(); - assertScenes(gd.layout, 'dragmode', 'zoom'); - expect(gd.layout.dragmode).toBe(undefined); - expect(gd._fullLayout.dragmode).toBe('zoom'); - expect(buttonTurntable.isActive()).toBe(false); - expect(buttonZoom3d.isActive()).toBe(true); - - buttonTurntable.click(); - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonZoom3d.isActive()).toBe(false); - }); + it('button zoom3d should updates the scene dragmode and dragmode button', function() { + var buttonTurntable = selectButton(modeBar, 'tableRotation'), + buttonZoom3d = selectButton(modeBar, 'zoom3d'); + + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonZoom3d.isActive()).toBe(false); + + buttonZoom3d.click(); + assertScenes(gd.layout, 'dragmode', 'zoom'); + expect(gd.layout.dragmode).toBe(undefined); + expect(gd._fullLayout.dragmode).toBe('zoom'); + expect(buttonTurntable.isActive()).toBe(false); + expect(buttonZoom3d.isActive()).toBe(true); + + buttonTurntable.click(); + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonZoom3d.isActive()).toBe(false); }); - describe('button pan3d', function() { - it('should updates the scene dragmode and dragmode button', function() { - var buttonTurntable = selectButton(modeBar, 'tableRotation'), - buttonPan3d = selectButton(modeBar, 'pan3d'); - - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonPan3d.isActive()).toBe(false); - - buttonPan3d.click(); - assertScenes(gd.layout, 'dragmode', 'pan'); - expect(gd.layout.dragmode).toBe(undefined); - expect(gd._fullLayout.dragmode).toBe('zoom'); - expect(buttonTurntable.isActive()).toBe(false); - expect(buttonPan3d.isActive()).toBe(true); - - buttonTurntable.click(); - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonPan3d.isActive()).toBe(false); - }); + it('button pan3d should updates the scene dragmode and dragmode button', function() { + var buttonTurntable = selectButton(modeBar, 'tableRotation'), + buttonPan3d = selectButton(modeBar, 'pan3d'); + + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonPan3d.isActive()).toBe(false); + + buttonPan3d.click(); + assertScenes(gd.layout, 'dragmode', 'pan'); + expect(gd.layout.dragmode).toBe(undefined); + expect(gd._fullLayout.dragmode).toBe('zoom'); + expect(buttonTurntable.isActive()).toBe(false); + expect(buttonPan3d.isActive()).toBe(true); + + buttonTurntable.click(); + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonPan3d.isActive()).toBe(false); }); - describe('button orbitRotation', function() { - it('should updates the scene dragmode and dragmode button', function() { - var buttonTurntable = selectButton(modeBar, 'tableRotation'), - buttonOrbit = selectButton(modeBar, 'orbitRotation'); - - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonOrbit.isActive()).toBe(false); - - buttonOrbit.click(); - assertScenes(gd.layout, 'dragmode', 'orbit'); - expect(gd.layout.dragmode).toBe(undefined); - expect(gd._fullLayout.dragmode).toBe('zoom'); - expect(buttonTurntable.isActive()).toBe(false); - expect(buttonOrbit.isActive()).toBe(true); - - buttonTurntable.click(); - assertScenes(gd._fullLayout, 'dragmode', 'turntable'); - expect(buttonTurntable.isActive()).toBe(true); - expect(buttonOrbit.isActive()).toBe(false); - }); + it('button orbitRotation should updates the scene dragmode and dragmode button', function() { + var buttonTurntable = selectButton(modeBar, 'tableRotation'), + buttonOrbit = selectButton(modeBar, 'orbitRotation'); + + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonOrbit.isActive()).toBe(false); + + buttonOrbit.click(); + assertScenes(gd.layout, 'dragmode', 'orbit'); + expect(gd.layout.dragmode).toBe(undefined); + expect(gd._fullLayout.dragmode).toBe('zoom'); + expect(buttonTurntable.isActive()).toBe(false); + expect(buttonOrbit.isActive()).toBe(true); + + buttonTurntable.click(); + assertScenes(gd._fullLayout, 'dragmode', 'turntable'); + expect(buttonTurntable.isActive()).toBe(true); + expect(buttonOrbit.isActive()).toBe(false); + }); + + it('button hoverClosest3d should update the scene hovermode and spikes', function() { + var buttonHover = selectButton(modeBar, 'hoverClosest3d'); + + assertScenes(gd._fullLayout, 'hovermode', 'closest'); + expect(buttonHover.isActive()).toBe(true); + + buttonHover.click(); + assertScenes(gd._fullLayout, 'hovermode', false); + assertScenes(gd._fullLayout, 'xaxis.showspikes', false); + assertScenes(gd._fullLayout, 'yaxis.showspikes', false); + assertScenes(gd._fullLayout, 'zaxis.showspikes', false); + expect(buttonHover.isActive()).toBe(false); + + buttonHover.click(); + assertScenes(gd._fullLayout, 'hovermode', 'closest'); + assertScenes(gd._fullLayout, 'xaxis.showspikes', true); + assertScenes(gd._fullLayout, 'yaxis.showspikes', true); + assertScenes(gd._fullLayout, 'zaxis.showspikes', true); + expect(buttonHover.isActive()).toBe(true); }); - describe('button hoverClosest3d', function() { - it('should update the scene hovermode and spikes', function() { - var buttonHover = selectButton(modeBar, 'hoverClosest3d'); - - assertScenes(gd._fullLayout, 'hovermode', 'closest'); - expect(buttonHover.isActive()).toBe(true); - - buttonHover.click(); - assertScenes(gd._fullLayout, 'hovermode', false); - assertScenes(gd._fullLayout, 'xaxis.showspikes', false); - assertScenes(gd._fullLayout, 'yaxis.showspikes', false); - assertScenes(gd._fullLayout, 'zaxis.showspikes', false); - expect(buttonHover.isActive()).toBe(false); - - buttonHover.click(); - assertScenes(gd._fullLayout, 'hovermode', 'closest'); - assertScenes(gd._fullLayout, 'xaxis.showspikes', true); - assertScenes(gd._fullLayout, 'yaxis.showspikes', true); - assertScenes(gd._fullLayout, 'zaxis.showspikes', true); - expect(buttonHover.isActive()).toBe(true); }); }); }); From 5aea58cc9841368a793e4d828d355a8c27d25768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 8 Feb 2017 15:46:01 -0500 Subject: [PATCH 6/6] add test case for 3d mode bar buttons --- test/jasmine/tests/gl_plot_interact_test.js | 102 ++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/test/jasmine/tests/gl_plot_interact_test.js b/test/jasmine/tests/gl_plot_interact_test.js index 25779564edc..2a7960b93e3 100644 --- a/test/jasmine/tests/gl_plot_interact_test.js +++ b/test/jasmine/tests/gl_plot_interact_test.js @@ -488,7 +488,109 @@ describe('Test gl plot interactions', function() { expect(buttonHover.isActive()).toBe(true); }); + it('button resetCameraDefault3d should reset camera to default', function(done) { + var buttonDefault = selectButton(modeBar, 'resetCameraDefault3d'); + + expect(gd._fullLayout.scene._scene.cameraInitial.eye).toEqual({ x: 0.1, y: 0.1, z: 1 }); + expect(gd._fullLayout.scene2._scene.cameraInitial.eye).toEqual({ x: 2.5, y: 2.5, z: 2.5 }); + + gd.once('plotly_relayout', function() { + assertScenes(gd._fullLayout, 'camera.eye.x', 1.25); + assertScenes(gd._fullLayout, 'camera.eye.y', 1.25); + assertScenes(gd._fullLayout, 'camera.eye.z', 1.25); + + expect(gd._fullLayout.scene._scene.getCamera().eye.z).toBeCloseTo(1.25); + expect(gd._fullLayout.scene2._scene.getCamera().eye.z).toBeCloseTo(1.25); + + done(); }); + + buttonDefault.click(); + }); + + it('button resetCameraLastSave3d should reset camera to default', function(done) { + var buttonDefault = selectButton(modeBar, 'resetCameraDefault3d'); + var buttonLastSave = selectButton(modeBar, 'resetCameraLastSave3d'); + + function assertCameraEye(sceneLayout, eyeX, eyeY, eyeZ) { + expect(sceneLayout.camera.eye.x).toEqual(eyeX); + expect(sceneLayout.camera.eye.y).toEqual(eyeY); + expect(sceneLayout.camera.eye.z).toEqual(eyeZ); + + var camera = sceneLayout._scene.getCamera(); + expect(camera.eye.x).toBeCloseTo(eyeX); + expect(camera.eye.y).toBeCloseTo(eyeY); + expect(camera.eye.z).toBeCloseTo(eyeZ); + } + + Plotly.relayout(gd, { + 'scene.camera.eye.z': 4, + 'scene2.camera.eye.z': 5 + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 4); + assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 5); + + return new Promise(function(resolve) { + gd.once('plotly_relayout', resolve); + buttonLastSave.click(); + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 1); + assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 2.5); + + return new Promise(function(resolve) { + gd.once('plotly_relayout', resolve); + buttonDefault.click(); + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 1.25, 1.25, 1.25); + assertCameraEye(gd._fullLayout.scene2, 1.25, 1.25, 1.25); + + return new Promise(function(resolve) { + gd.once('plotly_relayout', resolve); + buttonLastSave.click(); + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 1); + assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 2.5); + + delete gd._fullLayout.scene._scene.cameraInitial; + delete gd._fullLayout.scene2._scene.cameraInitial; + + Plotly.relayout(gd, { + 'scene.bgcolor': '#d3d3d3', + 'scene.camera.eye.z': 4, + 'scene2.camera.eye.z': 5 + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 4); + assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 5); + + return new Promise(function(resolve) { + gd.once('plotly_relayout', resolve); + buttonDefault.click(); + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 1.25, 1.25, 1.25); + assertCameraEye(gd._fullLayout.scene2, 1.25, 1.25, 1.25); + + return new Promise(function(resolve) { + gd.once('plotly_relayout', resolve); + buttonLastSave.click(); + }); + }) + .then(function() { + assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 4); + assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 5); + }) + .then(done); + }); });