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

Zoom to mouse cursor #2810

Merged
merged 25 commits into from
Jun 17, 2015
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a1895d9
Initial camera zoom to mouse cursor.
bagnell Jun 8, 2015
7aebc5f
Merge branch 'camera-mode' into zoom-to
bagnell Jun 8, 2015
0fa31b6
Rotate towards zoom point WIP.
bagnell Jun 9, 2015
90abd1a
Continued zoom to mouse position WIP.
bagnell Jun 10, 2015
4ead976
Fix zoom to mouse position in Columbus view.
bagnell Jun 10, 2015
701ac1d
Fix zoom rotation when camera is not oriented towards north.
bagnell Jun 10, 2015
2b17c90
Merge branch 'master' into zoom-to
bagnell Jun 11, 2015
4f52616
Zoom to mouse in 2D WIP.
bagnell Jun 11, 2015
1925d1e
Fix camera shake when zooming in 2D.
bagnell Jun 11, 2015
e44f585
Improved zoom speed in 2D.
bagnell Jun 11, 2015
069c8b5
Improved zoom to the edges of the viewport.
bagnell Jun 11, 2015
cb16671
Fix for slight camera bouncing and some code clean up.
bagnell Jun 11, 2015
d7952d2
More clean up. Use scratch variables. Another solution to the camera …
bagnell Jun 11, 2015
a521d19
Fix zoom tests.
bagnell Jun 11, 2015
4ddd8b5
Merge branch 'master' into zoom-to
bagnell Jun 12, 2015
b80b94a
Update CHANGES.md.
bagnell Jun 12, 2015
35b3c19
Improved 2D zoom.
bagnell Jun 15, 2015
164734c
Improve 3D zoom.
bagnell Jun 15, 2015
3f750bd
Another slight inprovement to 3D zoom and clean up code after the rew…
bagnell Jun 16, 2015
bdf51cb
Fix broken inertia when the camera is below terrain.
bagnell Jun 16, 2015
c077a1f
Fix tests.
bagnell Jun 16, 2015
10dca67
Change 3D zoom behavior after camera reaches a certain height and til…
bagnell Jun 16, 2015
3cea7ac
Fix error where acos of values slightly greater than 1.0 return NaN.
bagnell Jun 16, 2015
b7f1f87
Merge branch 'master' into zoom-to
bagnell Jun 16, 2015
0d70af3
Fix crash where picking the center of the screen does not hit terrain…
bagnell Jun 16, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Change Log
* Fixed an issue where `Camera` functions would throw an exception if used from within a `Scene.morphComplete` callback [#2776](https://github.com/AnalyticalGraphicsInc/cesium/issues/2776).
* `Model` can now load Binary glTF from a Uint8Array.
* Added a new camera mode for horizon views. When the camera is looking at the horizon and a point on terrain above the camera is picked, the camera moves in the plane containing the camera position, up and right vectors.
* The camera now zooms to the point under the mouse cursor.

### 1.10 - 2015-06-01

Expand Down
133 changes: 126 additions & 7 deletions Source/Scene/ScreenSpaceCameraController.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ define([
'./CameraEventAggregator',
'./CameraEventType',
'./SceneMode',
'./SceneTransforms',
'./TweenCollection'
], function(
Cartesian2,
Expand All @@ -46,6 +47,7 @@ define([
CameraEventAggregator,
CameraEventType,
SceneMode,
SceneTransforms,
TweenCollection) {
"use strict";

Expand Down Expand Up @@ -235,7 +237,7 @@ define([
* @type {Number}
* @default 10000.0
*/
this.minimumCollisionTerrainHeight = 10000.0;
this.minimumCollisionTerrainHeight = 15000.0;
/**
* The minimum height the camera must be before switching from rotating a track ball to
* free look when clicks originate on the sky on in space.
Expand Down Expand Up @@ -272,6 +274,8 @@ define([
this._rotateMousePosition = new Cartesian2(-1.0, -1.0);
this._rotateStartPosition = new Cartesian3();
this._strafeStartPosition = new Cartesian3();
this._zoomMouseStart = new Cartesian2();
this._zoomWorldPosition = new Cartesian3();
this._tiltCVOffMap = false;
this._looking = false;
this._rotating = false;
Expand Down Expand Up @@ -404,6 +408,16 @@ define([
}
}

var scratchZoomPickRay = new Ray();
var scratchZoomEndRay = new Ray();
var scratchPickCartesian = new Cartesian3();
var scratchZoomOffset = new Cartesian2();
var scratchZoomCartographic = new Cartographic();
var scratch2DCartesian = new Cartesian3();
var scratchStartWC = new Cartesian2();
var scratchEndClone = new Cartesian2();
var scratchZoomDirection = new Cartesian3();

function handleZoom(object, startPosition, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) {
var percentage = 1.0;
if (defined(unitPositionDotDirection)) {
Expand Down Expand Up @@ -438,24 +452,129 @@ define([
distance = distanceMeasure - maxHeight;
}

object._scene.camera.zoomIn(distance);
var scene = object._scene;
var camera = scene.camera;
var mode = scene.mode;

var pickedPosition = mode !== SceneMode.SCENE2D ? pickGlobe(object, startPosition, scratchPickCartesian) : camera.getPickRay(startPosition, scratchZoomPickRay).origin;

if (distance <= 0.0 || !defined(pickedPosition)) {
camera.zoomIn(distance);
return;
}

var zoomAlongVector = mode !== SceneMode.SCENE2D;

if ((camera.positionCartographic.height > 1000000.0 && mode === SceneMode.SCENE3D) || mode === SceneMode.SCENE2D) {
if (!Cartesian2.equals(startPosition, object._zoomMouseStart)) {
object._zoomMouseStart = Cartesian2.clone(startPosition, object._zoomMouseStart);
object._zoomWorldPosition = Cartesian3.clone(pickedPosition, object._zoomWorldPosition);
}

var canvas = scene.canvas;
var offset = scratchZoomOffset;
offset.x = canvas.clientWidth / 2;
offset.y = canvas.clientHeight / 2;

var startWorldPosition = object._zoomWorldPosition;
if (mode === SceneMode.SCENE2D) {
var projection = scene.mapProjection;
var ellipsoid = projection.ellipsoid;

var cartographic = projection.unproject(startWorldPosition, scratchZoomCartographic);
startWorldPosition = ellipsoid.cartographicToCartesian(cartographic, scratch2DCartesian);
}
var start = SceneTransforms.wgs84ToWindowCoordinates(scene, startWorldPosition, scratchStartWC);

var end = Cartesian2.clone(movement.endPosition, scratchEndClone);
end.x = movement.startPosition.x;

Cartesian2.subtract(offset, start, offset);

if (Cartesian2.magnitude(offset) > 20.0) {
var magnitude = Cartesian2.distance(movement.startPosition, end);
Cartesian2.normalize(offset, offset);
Cartesian2.multiplyByScalar(offset, magnitude, offset);

end.x = start.x + offset.x;
end.y = start.y + offset.y;

if (mode === SceneMode.SCENE2D) {
var worldPosition = camera.getPickRay(start, scratchZoomPickRay).origin;
var endPosition = camera.getPickRay(end, scratchZoomEndRay).origin;

var direction = Cartesian3.subtract(worldPosition, endPosition, scratchZoomDirection);
Cartesian3.normalize(direction, direction);

var zScale = Math.min(1.0, Math.max(0.25, (1.0 - camera.getMagnitude() / camera.position.z)));
var widthScale = Math.abs(start.x - (canvas.clientWidth * 0.5)) / canvas.clientWidth * 0.5;
var distanceScale = zScale + widthScale;
camera.move(direction, distance * distanceScale);
} else {
var rho = Cartesian3.magnitude(camera.position);
var rotateRate = object._rotateFactor * (rho - object._rotateRateRangeAdjustment);

if (rotateRate > object._maximumRotateRate) {
rotateRate = object._maximumRotateRate;
}

if (rotateRate < object._minimumRotateRate) {
rotateRate = object._minimumRotateRate;
}

var phiWindowRatio = (start.x - end.x) / canvas.clientWidth;
var thetaWindowRatio = (start.y - end.y) / canvas.clientHeight;

phiWindowRatio = Math.min(phiWindowRatio, object.maximumMovementRatio);
thetaWindowRatio = Math.min(thetaWindowRatio, object.maximumMovementRatio);

var deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0;
var deltaTheta = rotateRate * thetaWindowRatio * Math.PI;

var constrainedAxis = camera.constrainedAxis;
camera.constrainedAxis = undefined;

camera.rotateRight(deltaPhi);
camera.rotateUp(deltaTheta);

camera.constrainedAxis = constrainedAxis;
}

camera.zoomIn(distance);
zoomAlongVector = false;
}
}

if (zoomAlongVector) {
var ray;
if (Cartesian2.equals(startPosition, object._zoomMouseStart)) {
ray = camera.getPickRay(SceneTransforms.wgs84ToWindowCoordinates(scene, object._zoomWorldPosition, scratchZoomOffset), scratchZoomPickRay);
} else {
ray = camera.getPickRay(startPosition, scratchZoomPickRay);
}

var rayDirection = ray.direction;
if (mode === SceneMode.COLUMBUS_VIEW) {
Cartesian3.fromElements(rayDirection.y, rayDirection.z, rayDirection.x, rayDirection);
}

camera.move(rayDirection, distance);
} else if (mode === SceneMode.SCENE2D) {
camera.zoomIn(distance);
}
}

var translate2DStart = new Ray();
var translate2DEnd = new Ray();
var scratchTranslateP0 = new Cartesian3();
var scratchTranslateP1 = new Cartesian3();

function translate2D(controller, startPosition, movement) {
var scene = controller._scene;
var camera = scene.camera;
var start = camera.getPickRay(movement.startPosition, translate2DStart).origin;
var end = camera.getPickRay(movement.endPosition, translate2DEnd).origin;

var position = camera.position;
var p0 = Cartesian3.subtract(start, position, scratchTranslateP0);
var p1 = Cartesian3.subtract(end, position, scratchTranslateP1);
var direction = Cartesian3.subtract(p0, p1, scratchTranslateP0);
var direction = Cartesian3.subtract(start, end, scratchTranslateP0);
var distance = Cartesian3.magnitude(direction);

if (distance > 0.0) {
Expand Down
17 changes: 16 additions & 1 deletion Specs/Scene/ScreenSpaceCameraControllerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ defineSuite([
var ellipsoid = Ellipsoid.WGS84;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this enough tests to cover all the new code?

scene.mapProjection = new GeographicProjection(ellipsoid);

scene.frameState = {
mode : scene.mode,
mapProjection : scene.mapProjection
};

var maxRadii = ellipsoid.maximumRadius;
var frustum = new OrthographicFrustum();
frustum.right = maxRadii * Math.PI;
Expand All @@ -183,6 +188,11 @@ defineSuite([
var ellipsoid = Ellipsoid.WGS84;
scene.mapProjection = new GeographicProjection(ellipsoid);

scene.frameState = {
mode : scene.mode,
mapProjection : scene.mapProjection
};

var maxRadii = ellipsoid.maximumRadius;
camera.position = new Cartesian3(0.0, 0.0, maxRadii);
camera.direction = Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3());
Expand All @@ -195,6 +205,11 @@ defineSuite([

var ellipsoid = Ellipsoid.WGS84;
scene.mapProjection = new GeographicProjection(ellipsoid);

scene.frameState = {
mode : scene.mode,
mapProjection : scene.mapProjection
};
}

it('constructor throws without a scene', function() {
Expand Down Expand Up @@ -286,7 +301,7 @@ defineSuite([
moveMouse(MouseButtons.RIGHT, startPosition, endPosition);
updateController();
expect(position.x).toEqual(camera.position.x);
expect(position.y).toEqual(camera.position.y);
expect(position.y).toBeLessThan(camera.position.y);
expect(position.z).toEqual(camera.position.z);
expect(frustumDiff).toBeGreaterThan(camera.frustum.right - camera.frustum.left);
});
Expand Down