Skip to content

Commit cff31df

Browse files
authored
Merge pull request #3597 from plotly/fix-reset-camera3d-after-switch-projection
fix reset camera buttons to work after switching projection between perspective and orthographic
2 parents 1fd9560 + 3c37554 commit cff31df

File tree

5 files changed

+134
-21
lines changed

5 files changed

+134
-21
lines changed

src/components/modebar/buttons.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -347,14 +347,14 @@ function handleCamera3d(gd, ev) {
347347
var key = sceneId + '.camera';
348348
var scene = fullLayout[sceneId]._scene;
349349

350-
if(attr === 'resetDefault') {
351-
aobj[key] = Lib.extendDeep({}, scene.cameraInitial);
352-
aobj[key].up = null;
353-
aobj[key].eye = null;
354-
aobj[key].center = null;
355-
}
356-
else if(attr === 'resetLastSave') {
357-
aobj[key] = Lib.extendDeep({}, scene.cameraInitial);
350+
if(attr === 'resetLastSave') {
351+
aobj[key + '.up'] = scene.viewInitial.up;
352+
aobj[key + '.eye'] = scene.viewInitial.eye;
353+
aobj[key + '.center'] = scene.viewInitial.center;
354+
} else if(attr === 'resetDefault') {
355+
aobj[key + '.up'] = null;
356+
aobj[key + '.eye'] = null;
357+
aobj[key + '.center'] = null;
358358
}
359359
}
360360

src/plot_api/plot_api.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ function _restyle(gd, aobj, traces) {
15891589
// and figure out what kind of graphics update we need to do
15901590
for(var ai in aobj) {
15911591
if(helpers.hasParent(aobj, ai)) {
1592-
throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
1592+
throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
15931593
}
15941594

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

21012101
var p = layoutNP(layout, ai);

src/plots/gl3d/index.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,25 @@ exports.plot = function plotGl3d(gd) {
6767
sceneLayout._scene = scene;
6868
}
6969

70-
// save 'initial' camera settings for modebar button
71-
if(!scene.cameraInitial) {
72-
scene.cameraInitial = Lib.extendDeep({}, sceneLayout.camera);
70+
// save 'initial' camera view settings for modebar button
71+
if(!scene.viewInitial) {
72+
scene.viewInitial = {
73+
up: {
74+
x: camera.up.x,
75+
y: camera.up.y,
76+
z: camera.up.z
77+
},
78+
eye: {
79+
x: camera.eye.x,
80+
y: camera.eye.y,
81+
z: camera.eye.z
82+
},
83+
center: {
84+
x: camera.center.x,
85+
y: camera.center.y,
86+
z: camera.center.z
87+
}
88+
};
7389
}
7490

7591
scene.plot(fullSceneData, fullLayout, gd.layout);

src/plots/gl3d/scene.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ function initializeGLPlot(scene, camera, pixelRatio, canvas, gl) {
255255
if(scene.fullSceneLayout.dragmode === false) return;
256256

257257
var update = {};
258-
update[scene.id + '.camera'] = getLayoutCamera(scene.camera, scene.camera._ortho);
258+
update[scene.id + '.camera'] = getLayoutCamera(scene.camera);
259259
scene.saveCamera(gd.layout);
260260
scene.graphDiv.emit('plotly_relayout', update);
261261
};
@@ -758,19 +758,19 @@ function getOrbitCamera(camera) {
758758

759759
// getLayoutCamera :: orbit_camera_coords -> plotly_coords
760760
// inverse of getOrbitCamera
761-
function getLayoutCamera(camera, isOrtho) {
761+
function getLayoutCamera(camera) {
762762
return {
763763
up: {x: camera.up[0], y: camera.up[1], z: camera.up[2]},
764764
center: {x: camera.center[0], y: camera.center[1], z: camera.center[2]},
765765
eye: {x: camera.eye[0], y: camera.eye[1], z: camera.eye[2]},
766-
projection: {type: (isOrtho === true) ? 'orthographic' : 'perspective'}
766+
projection: {type: (camera._ortho === true) ? 'orthographic' : 'perspective'}
767767
};
768768
}
769769

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

776776
// set camera position with a set of plotly coords

test/jasmine/tests/gl3d_plot_interact_test.js

+101-4
Original file line numberDiff line numberDiff line change
@@ -1043,8 +1043,8 @@ describe('Test gl3d modebar handlers', function() {
10431043
it('@gl button resetCameraDefault3d should reset camera to default', function(done) {
10441044
var buttonDefault = selectButton(modeBar, 'resetCameraDefault3d');
10451045

1046-
expect(gd._fullLayout.scene._scene.cameraInitial.eye).toEqual({ x: 0.1, y: 0.1, z: 1 });
1047-
expect(gd._fullLayout.scene2._scene.cameraInitial.eye).toEqual({ x: 2.5, y: 2.5, z: 2.5 });
1046+
expect(gd._fullLayout.scene._scene.viewInitial.eye).toEqual({ x: 0.1, y: 0.1, z: 1 });
1047+
expect(gd._fullLayout.scene2._scene.viewInitial.eye).toEqual({ x: 2.5, y: 2.5, z: 2.5 });
10481048

10491049
gd.once('plotly_relayout', function() {
10501050
assertScenes(gd._fullLayout, 'camera.eye.x', 1.25);
@@ -1099,8 +1099,8 @@ describe('Test gl3d modebar handlers', function() {
10991099
assertCameraEye(gd._fullLayout.scene, 0.1, 0.1, 1);
11001100
assertCameraEye(gd._fullLayout.scene2, 2.5, 2.5, 2.5);
11011101

1102-
delete gd._fullLayout.scene._scene.cameraInitial;
1103-
delete gd._fullLayout.scene2._scene.cameraInitial;
1102+
delete gd._fullLayout.scene._scene.viewInitial;
1103+
delete gd._fullLayout.scene2._scene.viewInitial;
11041104

11051105
Plotly.relayout(gd, {
11061106
'scene.bgcolor': '#d3d3d3',
@@ -1489,6 +1489,103 @@ describe('Test gl3d relayout calls', function() {
14891489
.catch(failTest)
14901490
.then(done);
14911491
});
1492+
1493+
it('@gl should maintain projection type when resetCamera buttons clicked after switching projection type from perspective to orthographic', function(done) {
1494+
Plotly.plot(gd, {
1495+
data: [{
1496+
type: 'surface',
1497+
x: [0, 1],
1498+
y: [0, 1],
1499+
z: [[0, 1], [1, 0]]
1500+
}],
1501+
layout: {
1502+
width: 300,
1503+
height: 200,
1504+
scene: {
1505+
camera: {
1506+
eye: {
1507+
x: 2,
1508+
y: 1,
1509+
z: 0.5
1510+
}
1511+
}
1512+
}
1513+
}
1514+
})
1515+
.then(function() {
1516+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
1517+
})
1518+
.then(function() {
1519+
return Plotly.relayout(gd, 'scene.camera.projection.type', 'orthographic');
1520+
})
1521+
.then(function() {
1522+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
1523+
})
1524+
.then(function() {
1525+
return selectButton(gd._fullLayout._modeBar, 'resetCameraLastSave3d').click();
1526+
})
1527+
.then(function() {
1528+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
1529+
})
1530+
.then(function() {
1531+
return selectButton(gd._fullLayout._modeBar, 'resetCameraDefault3d').click();
1532+
})
1533+
.then(function() {
1534+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
1535+
})
1536+
.catch(failTest)
1537+
.then(done);
1538+
});
1539+
1540+
it('@gl should maintain projection type when resetCamera buttons clicked after switching projection type from orthographic to perspective', function(done) {
1541+
Plotly.plot(gd, {
1542+
data: [{
1543+
type: 'surface',
1544+
x: [0, 1],
1545+
y: [0, 1],
1546+
z: [[0, 1], [1, 0]]
1547+
}],
1548+
layout: {
1549+
width: 300,
1550+
height: 200,
1551+
scene: {
1552+
camera: {
1553+
eye: {
1554+
x: 2,
1555+
y: 1,
1556+
z: 0.5
1557+
},
1558+
projection: {
1559+
type: 'orthographic'
1560+
}
1561+
}
1562+
}
1563+
}
1564+
})
1565+
.then(function() {
1566+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(true, 'orthographic');
1567+
})
1568+
.then(function() {
1569+
return Plotly.relayout(gd, 'scene.camera.projection.type', 'perspective');
1570+
})
1571+
.then(function() {
1572+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
1573+
})
1574+
.then(function() {
1575+
return selectButton(gd._fullLayout._modeBar, 'resetCameraLastSave3d').click();
1576+
})
1577+
.then(function() {
1578+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
1579+
})
1580+
.then(function() {
1581+
return selectButton(gd._fullLayout._modeBar, 'resetCameraDefault3d').click();
1582+
})
1583+
.then(function() {
1584+
expect(gd._fullLayout.scene._scene.camera._ortho).toEqual(false, 'perspective');
1585+
})
1586+
.catch(failTest)
1587+
.then(done);
1588+
});
14921589
});
14931590

14941591
describe('Test gl3d annotations', function() {

0 commit comments

Comments
 (0)