Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix reset camera buttons to work after switching projection between perspective and orthographic #3597

Merged
merged 8 commits into from
Mar 5, 2019
Merged
23 changes: 16 additions & 7 deletions src/components/modebar/buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ modeBarButtons.resetCameraLastSave3d = {
function handleCamera3d(gd, ev) {
var button = ev.currentTarget;
var attr = button.getAttribute('data-attr');
if(attr !== 'resetLastSave' && attr !== 'resetDefault') return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When does this happen?


var fullLayout = gd._fullLayout;
var sceneIds = fullLayout._subplots.gl3d;
var aobj = {};
Expand All @@ -347,14 +349,21 @@ function handleCamera3d(gd, ev) {
var key = sceneId + '.camera';
var scene = fullLayout[sceneId]._scene;

if(attr === 'resetDefault') {
aobj[key] = Lib.extendDeep({}, scene.cameraInitial);
aobj[key].up = null;
aobj[key].eye = null;
aobj[key].center = null;
if(attr === 'resetLastSave') {
aobj[key + '.up'] = scene.viewInitial.up;
aobj[key + '.eye'] = scene.viewInitial.eye;
aobj[key + '.center'] = scene.viewInitial.center;
} else if(attr === 'resetDefault') {
aobj[key + '.up'] = null;
aobj[key + '.eye'] = null;
aobj[key + '.center'] = null;
}
else if(attr === 'resetLastSave') {
aobj[key] = Lib.extendDeep({}, scene.cameraInitial);

var newOrtho = (scene.viewInitial.projection.type === 'orthographic');
var oldOrtho = scene.camera._ortho;

if(newOrtho !== oldOrtho) {
aobj[key + '.projection'] = scene.viewInitial.projection;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait. Why do we need this?

}
}

Expand Down
4 changes: 2 additions & 2 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,7 @@ function _restyle(gd, aobj, traces) {
// and figure out what kind of graphics update we need to do
for(var ai in aobj) {
if(helpers.hasParent(aobj, ai)) {
throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
}

var vi = aobj[ai];
Expand Down Expand Up @@ -2095,7 +2095,7 @@ function _relayout(gd, aobj) {
// alter gd.layout
for(var ai in aobj) {
if(helpers.hasParent(aobj, ai)) {
throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
}

var p = layoutNP(layout, ai);
Expand Down
25 changes: 22 additions & 3 deletions src/plots/gl3d/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,28 @@ exports.plot = function plotGl3d(gd) {
sceneLayout._scene = scene;
}

// save 'initial' camera settings for modebar button
if(!scene.cameraInitial) {
scene.cameraInitial = Lib.extendDeep({}, sceneLayout.camera);
// save 'initial' camera view settings for modebar button
if(!scene.viewInitial) {
scene.viewInitial = {
projection: {
type: camera.projection.type
},
up: {
x: camera.up.x,
y: camera.up.y,
z: camera.up.z
},
eye: {
x: camera.eye.x,
y: camera.eye.y,
z: camera.eye.z
},
center: {
x: camera.center.x,
y: camera.center.y,
z: camera.center.z
}
};
}

scene.plot(fullSceneData, fullLayout, gd.layout);
Expand Down
8 changes: 4 additions & 4 deletions src/plots/gl3d/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ function initializeGLPlot(scene, camera, pixelRatio, canvas, gl) {
if(scene.fullSceneLayout.dragmode === false) return;

var update = {};
update[scene.id + '.camera'] = getLayoutCamera(scene.camera, scene.camera._ortho);
update[scene.id + '.camera'] = getLayoutCamera(scene.camera);
scene.saveCamera(gd.layout);
scene.graphDiv.emit('plotly_relayout', update);
};
Expand Down Expand Up @@ -758,19 +758,19 @@ function getOrbitCamera(camera) {

// getLayoutCamera :: orbit_camera_coords -> plotly_coords
// inverse of getOrbitCamera
function getLayoutCamera(camera, isOrtho) {
function getLayoutCamera(camera) {
return {
up: {x: camera.up[0], y: camera.up[1], z: camera.up[2]},
center: {x: camera.center[0], y: camera.center[1], z: camera.center[2]},
eye: {x: camera.eye[0], y: camera.eye[1], z: camera.eye[2]},
projection: {type: (isOrtho === true) ? 'orthographic' : 'perspective'}
projection: {type: (camera._ortho === true) ? 'orthographic' : 'perspective'}
};
}

// get camera position in plotly coords from 'orbit-camera' coords
proto.getCamera = function getCamera() {
this.glplot.camera.view.recalcMatrix(this.camera.view.lastT());
return getLayoutCamera(this.glplot.camera, this.glplot.camera._ortho);
return getLayoutCamera(this.glplot.camera);
};

// set camera position with a set of plotly coords
Expand Down
105 changes: 101 additions & 4 deletions test/jasmine/tests/gl3d_plot_interact_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1043,8 +1043,8 @@ describe('Test gl3d modebar handlers', function() {
it('@gl 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 });
expect(gd._fullLayout.scene._scene.viewInitial.eye).toEqual({ x: 0.1, y: 0.1, z: 1 });
expect(gd._fullLayout.scene2._scene.viewInitial.eye).toEqual({ x: 2.5, y: 2.5, z: 2.5 });

gd.once('plotly_relayout', function() {
assertScenes(gd._fullLayout, 'camera.eye.x', 1.25);
Expand Down Expand Up @@ -1099,8 +1099,8 @@ describe('Test gl3d modebar handlers', 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;
delete gd._fullLayout.scene._scene.viewInitial;
delete gd._fullLayout.scene2._scene.viewInitial;

Plotly.relayout(gd, {
'scene.bgcolor': '#d3d3d3',
Expand Down Expand Up @@ -1489,6 +1489,103 @@ describe('Test gl3d relayout calls', function() {
.catch(failTest)
.then(done);
});

it('@gl resetCamera buttons should be able to reset projection type after switching projection type from perspective to orthographic', function(done) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you change your mind here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we could switch projections to initial projection I thought we may change this.
If the user is not happy with the changes made to the camera and wants to get back to initial/home view this allows that. Codepen.

Plotly.plot(gd, {
data: [{
type: 'surface',
x: [0, 1],
y: [0, 1],
z: [[0, 1], [1, 0]]
}],
layout: {
width: 300,
height: 200,
scene: {
camera: {
eye: {
x: 2,
y: 1,
z: 0.5
}
}
}
}
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
})
.then(function() {
return Plotly.relayout(gd, 'scene.camera.projection.type', 'orthographic');
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
})
.then(function() {
return selectButton(gd._fullLayout._modeBar, 'resetCameraLastSave3d').click();
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
})
.then(function() {
return selectButton(gd._fullLayout._modeBar, 'resetCameraDefault3d').click();
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
})
.catch(failTest)
.then(done);
});

it('@gl resetCamera buttons should be able to reset projection type after switching projection type from orthographic to perspective', function(done) {
Plotly.plot(gd, {
data: [{
type: 'surface',
x: [0, 1],
y: [0, 1],
z: [[0, 1], [1, 0]]
}],
layout: {
width: 300,
height: 200,
scene: {
camera: {
eye: {
x: 2,
y: 1,
z: 0.5
},
projection: {
type: 'orthographic'
}
}
}
}
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
})
.then(function() {
return Plotly.relayout(gd, 'scene.camera.projection.type', 'perspective');
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
})
.then(function() {
return selectButton(gd._fullLayout._modeBar, 'resetCameraLastSave3d').click();
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
})
.then(function() {
return selectButton(gd._fullLayout._modeBar, 'resetCameraDefault3d').click();
})
.then(function() {
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
})
.catch(failTest)
.then(done);
});
});

describe('Test gl3d annotations', function() {
Expand Down