Skip to content

Commit

Permalink
Merge pull request #2810 from AnalyticalGraphicsInc/zoom-to
Browse files Browse the repository at this point in the history
Zoom to mouse cursor
  • Loading branch information
pjcozzi committed Jun 17, 2015
2 parents c8bd90e + 0d70af3 commit 11ed792
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Change Log
* `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.
* Added `UrlTemplateImageryProvider`. This new imagery provider allows access to a wide variety of imagery sources, including OpenStreetMap, TMS, WMTS, WMS, WMS-C, and various custom schemes, by specifying a URL template to use to request imagery tiles.
* The camera now zooms to the point under the mouse cursor.

### 1.10 - 2015-06-01

Expand Down
102 changes: 93 additions & 9 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 @@ -397,13 +401,24 @@ define([
if (controller.enableInputs && enabled) {
if (movement) {
action(controller, startPosition, movement);
} else if (inertiaConstant < 1.0 && !controller._strafing) {
} else if (inertiaConstant < 1.0) {
maintainInertia(aggregator, type, modifier, inertiaConstant, action, controller, inertiaStateName);
}
}
}
}

var scratchZoomPickRay = new Ray();
var scratchPickCartesian = new Cartesian3();
var scratchZoomOffset = new Cartesian2();
var scratchZoomDirection = new Cartesian3();
var scratchCenterPixel = new Cartesian2();
var scratchCenterPosition = new Cartesian3();
var scratchPositionNormal = new Cartesian3();
var scratchPickNormal = new Cartesian3();
var scratchZoomAxis = new Cartesian3();
var scratchCameraPositionNormal = new Cartesian3();

function handleZoom(object, startPosition, movement, zoomFactor, distanceMeasure, unitPositionDotDirection) {
var percentage = 1.0;
if (defined(unitPositionDotDirection)) {
Expand Down Expand Up @@ -438,24 +453,90 @@ 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;
}

if (!Cartesian2.equals(startPosition, object._zoomMouseStart)) {
object._zoomMouseStart = Cartesian2.clone(startPosition, object._zoomMouseStart);
object._zoomWorldPosition = Cartesian3.clone(pickedPosition, object._zoomWorldPosition);
}

var zoomOnVector = mode === SceneMode.COLUMBUS_VIEW;

if (mode === SceneMode.SCENE2D) {
var worldPosition = object._zoomWorldPosition;
var endPosition = camera.position;

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

var d = Cartesian3.distance(worldPosition, endPosition) * distance / (camera.getMagnitude() * 0.5);
camera.move(direction, d * 0.5);
} else if (mode === SceneMode.SCENE3D) {

var cameraPositionNormal = Cartesian3.normalize(camera.position, scratchCameraPositionNormal);
if (camera.positionCartographic.height < 3000.0 && Math.abs(Cartesian3.dot(camera.direction, cameraPositionNormal)) < 0.6) {
zoomOnVector = true;
} else {
var canvas = scene.canvas;

var centerPixel = scratchCenterPixel;
centerPixel.x = canvas.clientWidth / 2;
centerPixel.y = canvas.clientHeight / 2;
var centerPosition = pickGlobe(object, centerPixel, scratchCenterPosition);
if (defined(centerPosition)) {
var positionNormal = Cartesian3.normalize(centerPosition, scratchPositionNormal);
var pickedNormal = Cartesian3.normalize(object._zoomWorldPosition, scratchPickNormal);
var angle = CesiumMath.acosClamped(Cartesian3.dot(pickedNormal, positionNormal));
var axis = Cartesian3.cross(pickedNormal, positionNormal, scratchZoomAxis);

var denom = Math.abs(angle) > CesiumMath.toRadians(20.0) ? camera.positionCartographic.height * 0.75 : camera.positionCartographic.height - distance;
var scalar = distance / denom;
camera.rotate(axis, angle * scalar);
} else {
zoomOnVector = true;
}
}
}

if (zoomOnVector) {
var ray;
var zoomMouseStart = SceneTransforms.wgs84ToWindowCoordinates(scene, object._zoomWorldPosition, scratchZoomOffset);
if (mode !== SceneMode.COLUMBUS_VIEW && Cartesian2.equals(startPosition, object._zoomMouseStart) && defined(zoomMouseStart)) {
ray = camera.getPickRay(zoomMouseStart, 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 {
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 Expand Up @@ -1018,7 +1099,10 @@ define([
var scene = controller._scene;
var camera = scene.camera;

var mouseStartPosition = controller._strafeStartPosition;
var mouseStartPosition = pickGlobe(controller, movement.startPosition, scratchMousePos);
if (!defined(mouseStartPosition)) {
return;
}

var mousePosition = movement.endPosition;
var ray = camera.getPickRay(mousePosition, scratchStrafeRay);
Expand Down
21 changes: 18 additions & 3 deletions 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;
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 Expand Up @@ -315,7 +330,7 @@ defineSuite([
updateController();
expect(position.x).toEqual(camera.position.x);
expect(position.y).toEqual(camera.position.y);
expect(position.z).toEqual(camera.position.z);
expect(position.z).toBeGreaterThan(camera.position.z);
expect(frustumDiff).toBeGreaterThan(camera.frustum.right - camera.frustum.left);
});

Expand Down Expand Up @@ -349,7 +364,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

0 comments on commit 11ed792

Please sign in to comment.