diff --git a/src/plot_api/helpers.js b/src/plot_api/helpers.js index 4866edcd6ec..cdb8177c60c 100644 --- a/src/plot_api/helpers.js +++ b/src/plot_api/helpers.js @@ -114,7 +114,7 @@ exports.cleanLayout = function(layout) { scene.camera = { eye: {x: eye[0], y: eye[1], z: eye[2]}, center: {x: center[0], y: center[1], z: center[2]}, - up: {x: mat[1], y: mat[5], z: mat[9]} + up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case }; delete scene.cameraposition; diff --git a/src/plots/gl3d/layout/defaults.js b/src/plots/gl3d/layout/defaults.js index 3b78eb1720a..3d208a09d04 100644 --- a/src/plots/gl3d/layout/defaults.js +++ b/src/plots/gl3d/layout/defaults.js @@ -110,6 +110,31 @@ function handleGl3dDefaults(sceneLayoutIn, sceneLayoutOut, coerce, opts) { sceneLayoutIn, sceneLayoutOut, opts ); - coerce('dragmode', opts.getDfltFromLayout('dragmode')); + var dragmode = opts.getDfltFromLayout('dragmode'); + + if(dragmode !== false) { + if(!dragmode) { + + dragmode = 'orbit'; + + if(sceneLayoutIn.camera && + sceneLayoutIn.camera.up) { + + var x = sceneLayoutIn.camera.up.x; + var y = sceneLayoutIn.camera.up.y; + var z = sceneLayoutIn.camera.up.z; + + if(!x || !y || !z) { + dragmode = 'turntable'; + } else if(z / Math.sqrt(x * x + y * y + z * z) > 0.999) { + dragmode = 'turntable'; + } + } else { + dragmode = 'turntable'; + } + } + } + + coerce('dragmode', dragmode); coerce('hovermode', opts.getDfltFromLayout('hovermode')); } diff --git a/src/plots/gl3d/layout/layout_attributes.js b/src/plots/gl3d/layout/layout_attributes.js index 870eca0151c..00b940885c4 100644 --- a/src/plots/gl3d/layout/layout_attributes.js +++ b/src/plots/gl3d/layout/layout_attributes.js @@ -140,7 +140,6 @@ module.exports = { valType: 'enumerated', role: 'info', values: ['orbit', 'turntable', 'zoom', 'pan', false], - dflt: 'turntable', editType: 'plot', description: [ 'Determines the mode of drag interactions for this scene.' diff --git a/test/image/baselines/gl3d_opacity-surface.png b/test/image/baselines/gl3d_opacity-surface.png index e200930b8fc..ad89c289b21 100644 Binary files a/test/image/baselines/gl3d_opacity-surface.png and b/test/image/baselines/gl3d_opacity-surface.png differ diff --git a/test/image/mocks/gl3d_text-weirdness.json b/test/image/mocks/gl3d_text-weirdness.json index 9ef88a6be3e..06c2fec949f 100644 --- a/test/image/mocks/gl3d_text-weirdness.json +++ b/test/image/mocks/gl3d_text-weirdness.json @@ -42,22 +42,6 @@ ], "layout": { "autosize": true, - "undefined": { - "cameraposition": [ - [ - 0.13855639464070454, - 0.5365430934822464, - 0.7678929376547012, - -0.32134727420767745 - ], - [ - 0, - 0, - 0 - ], - 1.8771354322421991 - ] - }, "title": "Scatter3d with text weirdness", "showlegend": false, "height": 758, diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index ffcaa7c9997..0031d74375d 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -392,6 +392,135 @@ describe('Test gl3d plots', function() { .then(done); }); + it('@gl should set the camera dragmode to orbit if the camera.up.z vector is set to be tilted', function(done) { + Plotly.plot(gd, { + data: [{ + type: 'scatter3d', + x: [1, 2, 3], + y: [2, 3, 1], + z: [3, 1, 2] + }], + layout: { + scene: { + camera: { + up: { + x: -0.5777, + y: -0.5777, + z: 0.5777 + } + } + } + } + }) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout.scene.dragmode === 'orbit').toBe(true); + }) + .then(done); + }); + + it('@gl should set the camera dragmode to turntable if the camera.up.z vector is set to be upwards', function(done) { + Plotly.plot(gd, { + data: [{ + type: 'scatter3d', + x: [1, 2, 3], + y: [2, 3, 1], + z: [3, 1, 2] + }], + layout: { + scene: { + camera: { + up: { + x: -0.0001, + y: 0, + z: 123.45 + } + } + } + } + }) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout.scene.dragmode === 'turntable').toBe(true); + }) + .then(done); + }); + + it('@gl should set the camera dragmode to turntable if the camera.up is not set', function(done) { + Plotly.plot(gd, { + data: [{ + type: 'scatter3d', + x: [1, 2, 3], + y: [2, 3, 1], + z: [3, 1, 2] + }], + layout: { + scene: { + camera: { + } + } + } + }) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout.scene.dragmode === 'turntable').toBe(true); + }) + .then(done); + }); + + it('@gl should set the camera dragmode to turntable if any of camera.up.[x|y|z] is missing', function(done) { + Plotly.plot(gd, { + data: [{ + type: 'scatter3d', + x: [1, 2, 3], + y: [2, 3, 1], + z: [3, 1, 2] + }], + layout: { + scene: { + camera: { + up: { + x: null, + z: 0 + } + } + } + } + }) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout.scene.dragmode === 'turntable').toBe(true); + }) + .then(done); + }); + + it('@gl should set the camera dragmode to turntable if all camera.up.[x|y|z] are zero or missing', function(done) { + Plotly.plot(gd, { + data: [{ + type: 'scatter3d', + x: [1, 2, 3], + y: [2, 3, 1], + z: [3, 1, 2] + }], + layout: { + scene: { + camera: { + up: { + x: 0, + y: 0, + z: 0 + } + } + } + } + }) + .then(delay(20)) + .then(function() { + expect(gd._fullLayout.scene.dragmode === 'turntable').toBe(true); + }) + .then(done); + }); + it('@gl should be able to reversibly change trace type', function(done) { var _mock = Lib.extendDeep({}, mock2); var sceneLayout = { aspectratio: { x: 1, y: 1, z: 1 } }; @@ -405,6 +534,7 @@ describe('Test gl3d plots', function() { expect(gd.layout.yaxis === undefined).toBe(true); expect(gd._fullLayout._has('gl3d')).toBe(true); expect(gd._fullLayout.scene._scene).toBeDefined(); + expect(gd._fullLayout.scene._scene.camera).toBeDefined(true); return Plotly.restyle(gd, 'type', 'scatter'); })