diff --git a/CHANGES.md b/CHANGES.md index d7f7c05f45c0..d426e64bde8d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,9 +9,13 @@ Change Log * The `sourceUri` parameter to `GeoJsonDatasource.load` was deprecated in Cesium 1.4 and has been removed. Use options.sourceUri instead. * `PolygonGraphics.positions` created by `GeoJSONDataSource` now evaluate to a `PolygonHierarchy` object instead of an array of positions. * Deprecated + * `Camera.tilt` was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use `Camera.pitch`. + * `Camera.heading` and `Camera.tilt` were deprecated in Cesium 1.6. They will become read-only in Cesium 1.7. Use `Camera.setView`. + * `Camera.setPositionCartographic` was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use `Camera.setView`. * `PolygonGraphics.positions` was deprecated and replaced with `PolygonGraphics.hierarchy`, whose value is a `PolygonHierarchy` instead of an array of positions. `PolygonGraphics.positions` will be removed in Cesium 1.8. * Improved performance of asynchronous geometry creation (as much as 20% faster in some use cases). [#2342](https://github.com/AnalyticalGraphicsInc/cesium/issues/2342) * Added `PolylineVolumeGraphics` and `Entity.polylineVolume` +* Added `Camera.roll`. * Added `Quaternion.fromHeadingPitchRoll` to create a rotation from heading, pitch, and roll angles. * Added `Transforms.headingPitchRollToFixedFrame` to create a local frame from a position and heading/pitch/roll angles. * Added `BillboardGraphics.imageSubRegion`, to enable custom texture atlas use for `Entity` instances. diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index 51cfa610a9cc..fb3449634c94 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -7,6 +7,7 @@ define([ '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', + '../Core/deprecationWarning', '../Core/DeveloperError', '../Core/EasingFunction', '../Core/Ellipsoid', @@ -29,6 +30,7 @@ define([ defaultValue, defined, defineProperties, + deprecationWarning, DeveloperError, EasingFunction, Ellipsoid, @@ -506,52 +508,8 @@ define([ } } - function getHeading2D(camera) { - return Math.atan2(camera.right.y, camera.right.x); - } - - var scratchHeadingMatrix4 = new Matrix4(); - var scratchHeadingMatrix3 = new Matrix3(); - var scratchHeadingCartesian3 = new Cartesian3(); - - function getHeading3D(camera) { - var ellipsoid = camera._projection.ellipsoid; - var toFixedFrame = Transforms.eastNorthUpToFixedFrame(camera.position, ellipsoid, scratchHeadingMatrix4); - var transform = Matrix4.getRotation(toFixedFrame, scratchHeadingMatrix3); - Matrix3.transpose(transform, transform); - - var right = Matrix3.multiplyByVector(transform, camera.right, scratchHeadingCartesian3); - return Math.atan2(right.y, right.x); - } - - function setHeading2D(camera, angle) { - var rightAngle = getHeading2D(camera); - angle = rightAngle - angle; - camera.look(Cartesian3.UNIT_Z, angle); - } - - var scratchHeadingAxis = new Cartesian3(); - - function setHeading3D(camera, angle) { - var axis = Cartesian3.normalize(camera.position, scratchHeadingAxis); - var upAngle = getHeading3D(camera); - angle = upAngle - angle; - camera.look(axis, angle); - } - - function getTiltCV(camera) { - // CesiumMath.acosClamped(dot(camera.direction, Cartesian3.negate(Cartesian3.UNIT_Z)) - return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(-camera.direction.z); - } - - var scratchTiltCartesian3 = new Cartesian3(); - - function getTilt3D(camera) { - var direction = Cartesian3.normalize(camera.position, scratchTiltCartesian3); - Cartesian3.negate(direction, direction); - - return CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(Cartesian3.dot(camera.direction, direction)); - } + var scratchHPRMatrix1 = new Matrix4(); + var scratchHPRMatrix2 = new Matrix4(); defineProperties(Camera.prototype, { /** @@ -675,23 +633,41 @@ define([ }, /** - * Gets or sets the camera heading in radians. + * Gets the camera heading in radians. * @memberof Camera.prototype * * @type {Number} */ heading : { get : function () { - if (this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW) { - return getHeading2D(this); - } else if (this._mode === SceneMode.SCENE3D) { - return getHeading3D(this); + if (this._mode !== SceneMode.MORPHING) { + var origin = this.positionWC; + var ellipsoid = this._projection.ellipsoid; + + var oldTransform = Matrix4.clone(this.transform, scratchHPRMatrix1); + var transform = Transforms.eastNorthUpToFixedFrame(this.positionWC, ellipsoid, scratchHPRMatrix2); + this.setTransform(transform); + + + var right = this.right; + var direction = this.direction; + + var heading; + if (Math.abs(direction.z) < Math.abs(right.z)) { + heading = Math.atan2(direction.y, direction.x) - CesiumMath.PI_OVER_TWO; + } else { + heading = Math.atan2(right.y, right.x); + } + + this.setTransform(oldTransform); + + return CesiumMath.zeroToTwoPi(heading); } return undefined; }, - //TODO See https://github.com/AnalyticalGraphicsInc/cesium/issues/832 set : function (angle) { + deprecationWarning('Camera.heading', 'Camera.heading was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use Camera.setView.'); //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { @@ -699,32 +675,54 @@ define([ } //>>includeEnd('debug'); - if (this._mode === SceneMode.SCENE2D || this._mode === SceneMode.COLUMBUS_VIEW) { - setHeading2D(this, angle); - } else if (this._mode === SceneMode.SCENE3D) { - setHeading3D(this, angle); + if (this._mode !== SceneMode.MORPHING) { + this.setView({ heading : angle }); } } }, /** - * Gets or sets the camera tilt in radians. + * Gets the camera pitch in radians. * @memberof Camera.prototype * * @type {Number} */ - tilt : { + pitch : { get : function() { - if (this._mode === SceneMode.COLUMBUS_VIEW) { - return getTiltCV(this); - } else if (this._mode === SceneMode.SCENE3D) { - return getTilt3D(this); + if (this._mode === SceneMode.COLUMBUS_VIEW || this._mode === SceneMode.SCENE3D) { + var origin = this.positionWC; + var ellipsoid = this._projection.ellipsoid; + + var oldTransform = Matrix4.clone(this.transform, scratchHPRMatrix1); + var transform = Transforms.eastNorthUpToFixedFrame(this.positionWC, ellipsoid, scratchHPRMatrix2); + this.setTransform(transform); + + var pitch = CesiumMath.PI_OVER_TWO - CesiumMath.acosClamped(-this.direction.z); + + this.setTransform(oldTransform); + + return pitch; } return undefined; + } + }, + + /** + * Gets or sets the camera tilt in radians. + * @memberof Camera.prototype + * + * @type {Number} + * + * @deprecated + */ + tilt : { + get : function() { + deprecationWarning('Camera.tilt', 'Camera.tilt was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use Camera.pitch.'); + return this.pitch; }, - //TODO See https://github.com/AnalyticalGraphicsInc/cesium/issues/832 set : function(angle) { + deprecationWarning('Camera.tilt', 'Camera.tilt was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use Camera.setView.'); //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { @@ -733,10 +731,41 @@ define([ //>>includeEnd('debug'); if (this._mode === SceneMode.COLUMBUS_VIEW || this._mode === SceneMode.SCENE3D) { - angle = CesiumMath.clamp(angle, -CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO); - angle = angle - this.tilt; - this.look(this.right, angle); + this.setView({ pitch : angle }); + } + } + }, + + /** + * Gets the camera roll in radians. + * @memberof Camera.prototype + * + * @type {Number} + */ + roll : { + get : function() { + if (this._mode === SceneMode.COLUMBUS_VIEW || this._mode === SceneMode.SCENE3D) { + var origin = this.positionWC; + var ellipsoid = this._projection.ellipsoid; + + var oldTransform = Matrix4.clone(this.transform, scratchHPRMatrix1); + var transform = Transforms.eastNorthUpToFixedFrame(this.positionWC, ellipsoid, scratchHPRMatrix2); + this.setTransform(transform); + + var up = this.up; + var right = this.right; + + var roll = Math.acos(-right.z) - CesiumMath.PI_OVER_TWO; + if (up.z < 0.0) { + roll = CesiumMath.PI - roll; + } + + this.setTransform(oldTransform); + + return CesiumMath.zeroToTwoPi(roll); } + + return undefined; } } }); @@ -801,6 +830,96 @@ define([ Cartesian3.cross(this.direction, this.up, this.right); }; + var scratchSetViewCartesian = new Cartesian3(); + var scratchSetViewTransform1 = new Matrix4(); + var scratchSetViewTransform2 = new Matrix4(); + var scratchSetViewQuaternion = new Quaternion(); + var scratchSetViewMatrix3 = new Matrix3(); + var scratchSetViewCartographic = new Cartographic(); + + /** + * Sets the camera position and orientation with heading, pitch and roll angles. + * + * The position can be given as either a cartesian or a cartographic. If both are given, + * then the cartesian will be used. If neither is given, then the current camera position + * will be used. + * + * @param {Cartesian3} [options.cartesian] The cartesian position of the camera. + * @param {Cartographic} [options.cartographic] The cartographic position of the camera. + * @param {Number} [options.heading] The heading in radians or the current heading will be used if undefined. + * @param {Number} [options.pitch] The pitch in radians or the current pitch will be used if undefined. + * @param {Number} [options.roll] The roll in radians or the current roll will be used if undefined. + */ + Camera.prototype.setView = function(options) { + if (this._mode === SceneMode.MORPHING) { + return; + } + + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var scene2D = this._mode === SceneMode.SCENE2D; + var scene3D = this._mode === SceneMode.SCENE3D; + + var heading = defaultValue(options.heading, this.heading); + var pitch = scene2D ? CesiumMath.PI_OVER_TWO : defaultValue(options.pitch, this.pitch); + var roll = scene2D ? 0.0 : defaultValue(options.roll, this.roll); + + if (scene3D) { + roll = -roll; + } + + var cartesian = options.cartesian; + var cartographic = options.cartographic; + + var projection = this._projection; + var ellipsoid = projection.ellipsoid; + + if (!defined(cartesian)) { + if (defined(cartographic)) { + cartesian = ellipsoid.cartographicToCartesian(cartographic, scratchSetViewCartesian); + } else { + cartesian = Cartesian3.clone(this.positionWC, scratchSetViewCartesian); + } + } + + var currentTransform = Matrix4.clone(this.transform, scratchSetViewTransform1); + var localTransform = Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, scratchSetViewTransform2); + this.setTransform(localTransform); + + if (scene2D) { + Cartesian2.clone(Cartesian3.ZERO, this.position); + + var cartographic2D = ellipsoid.cartesianToCartographic(cartesian, scratchSetViewCartographic); + var newLeft = -cartographic2D.height * 0.5; + var newRight = -newLeft; + + var frustum = this.frustum; + if (newRight > newLeft) { + var ratio = frustum.top / frustum.right; + frustum.right = newRight; + frustum.left = newLeft; + frustum.top = frustum.right * ratio; + frustum.bottom = -frustum.top; + } + } else { + Cartesian3.clone(Cartesian3.ZERO, this.position); + } + + var headingQuaternion = Quaternion.fromAxisAngle(Cartesian3.UNIT_Z, heading + CesiumMath.PI_OVER_TWO, new Quaternion()); + var pitchQuaternion = Quaternion.fromAxisAngle(Cartesian3.UNIT_Y, pitch, new Quaternion()); + var rotQuat = Quaternion.multiply(headingQuaternion, pitchQuaternion, headingQuaternion); + + var rollQuaternion = Quaternion.fromAxisAngle(Cartesian3.UNIT_X, roll, new Quaternion()); + Quaternion.multiply(rollQuaternion, rotQuat, rotQuat); + var rotMat = Matrix3.fromQuaternion(rotQuat, scratchSetViewMatrix3); + + Matrix3.getColumn(rotMat, 0, this.direction); + Matrix3.getColumn(rotMat, 2, this.up); + Cartesian3.cross(this.direction, this.up, this.right); + + this.setTransform(currentTransform); + }; + /** * Transform a vector or point from world coordinates to the camera's reference frame. * @@ -1369,64 +1488,28 @@ define([ } }; - function setPositionCartographic2D(camera, cartographic) { - var newLeft = -cartographic.height * 0.5; - var newRight = -newLeft; - - var frustum = camera.frustum; - if (newRight > newLeft) { - var ratio = frustum.top / frustum.right; - frustum.right = newRight; - frustum.left = newLeft; - frustum.top = frustum.right * ratio; - frustum.bottom = -frustum.top; - } - - //We use Cartesian2 instead of 3 here because Z must be constant in 2D mode. - Cartesian2.clone(camera._projection.project(cartographic), camera.position); - Cartesian3.negate(Cartesian3.UNIT_Z, camera.direction); - Cartesian3.clone(Cartesian3.UNIT_Y, camera.up); - Cartesian3.clone(Cartesian3.UNIT_X, camera.right); - } - - function setPositionCartographicCV(camera, cartographic) { - var projection = camera._projection; - camera.position = projection.project(cartographic); - Cartesian3.negate(Cartesian3.UNIT_Z, camera.direction); - Cartesian3.clone(Cartesian3.UNIT_Y, camera.up); - Cartesian3.clone(Cartesian3.UNIT_X, camera.right); - } - - function setPositionCartographic3D(camera, cartographic) { - var ellipsoid = camera._projection.ellipsoid; - - ellipsoid.cartographicToCartesian(cartographic, camera.position); - Cartesian3.negate(camera.position, camera.direction); - Cartesian3.normalize(camera.direction, camera.direction); - Cartesian3.cross(camera.direction, Cartesian3.UNIT_Z, camera.right); - Cartesian3.cross(camera.right, camera.direction, camera.up); - Cartesian3.cross(camera.direction, camera.up, camera.right); - } - /** * Moves the camera to the provided cartographic position. * + * @deprecated + * * @param {Cartographic} cartographic The new camera position. */ Camera.prototype.setPositionCartographic = function(cartographic) { + deprecationWarning('Camera.setPositionCartographic', 'Camera.setPositionCartographic was deprecated in Cesium 1.6. It will be removed in Cesium 1.7. Use Camera.setView.'); + //>>includeStart('debug', pragmas.debug); if (!defined(cartographic)) { throw new DeveloperError('cartographic is required.'); } //>>includeEnd('debug'); - if (this._mode === SceneMode.SCENE2D) { - setPositionCartographic2D(this, cartographic); - } else if (this._mode === SceneMode.COLUMBUS_VIEW) { - setPositionCartographicCV(this, cartographic); - } else if (this._mode === SceneMode.SCENE3D) { - setPositionCartographic3D(this, cartographic); - } + this.setView({ + cartographic : cartographic, + heading : 0.0, + pitch : CesiumMath.PI_OVER_TWO, + roll : 0.0 + }); }; /** diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js index 100dd19c4a8f..1869d28c0690 100644 --- a/Specs/Scene/CameraSpec.js +++ b/Specs/Scene/CameraSpec.js @@ -146,23 +146,19 @@ defineSuite([ Matrix3.transpose(transform, transform); var right = Matrix3.multiplyByVector(transform, camera.right, new Cartesian3()); - var heading = Math.atan2(right.y, right.x); + var heading = CesiumMath.zeroToTwoPi(Math.atan2(right.y, right.x)); expect(camera.heading).toEqual(heading); }); - it('set heading throws without angle', function() { - expect(function() { - camera.heading = undefined; - }).toThrowDeveloperError(); - }); - it('set heading in 2D', function() { camera._mode = SceneMode.SCENE2D; var heading = camera.heading; var newHeading = CesiumMath.toRadians(45.0); - camera.heading = newHeading; + camera.setView({ + heading : newHeading + }); expect(camera.heading).not.toEqual(heading); expect(camera.heading).toEqualEpsilon(newHeading, CesiumMath.EPSILON14); @@ -171,9 +167,16 @@ defineSuite([ it('set heading in CV', function() { camera._mode = SceneMode.COLUMBUS_VIEW; + camera.position = Cartesian3.fromDegrees(0.0, 0.0, 100000.0); + camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); + camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); + var heading = camera.heading; var newHeading = CesiumMath.toRadians(45.0); - camera.heading = newHeading; + camera.setView({ + heading : newHeading + }); expect(camera.heading).not.toEqual(heading); expect(camera.heading).toEqualEpsilon(newHeading, CesiumMath.EPSILON14); @@ -189,65 +192,147 @@ defineSuite([ var heading = camera.heading; var newHeading = CesiumMath.toRadians(45.0); - camera.heading = newHeading; + camera.setView({ + heading : newHeading + }); expect(camera.heading).not.toEqual(heading); expect(camera.heading).toEqualEpsilon(newHeading, CesiumMath.EPSILON14); }); - it('tilt is undefined when mode is not 3D or Columbus view', function() { + it('pitch is undefined when mode is not 3D or Columbus view', function() { camera._mode = SceneMode.MORPHING; - expect(camera.tilt).not.toBeDefined(); + expect(camera.pitch).not.toBeDefined(); }); - it('get tilt in CV', function() { + it('get pitch in CV', function() { camera._mode = SceneMode.COLUMBUS_VIEW; - var tilt = CesiumMath.PI_OVER_TWO - Math.acos(-camera.direction.z); - expect(camera.tilt).toEqual(tilt); + camera.position = Cartesian3.fromDegrees(0.0, 0.0, 100000.0); + camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); + camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); + + var pitch = CesiumMath.PI_OVER_TWO - Math.acos(-camera.direction.z); + expect(camera.pitch).toEqualEpsilon(pitch, CesiumMath.EPSILON6); }); - it('get tilt in 3D', function() { + it('get pitch in 3D', function() { camera._mode = SceneMode.SCENE3D; var direction = Cartesian3.normalize(camera.position, new Cartesian3()); Cartesian3.negate(direction, direction); - var tilt = CesiumMath.PI_OVER_TWO - Math.acos(Cartesian3.dot(camera.direction, direction)); + var pitch = CesiumMath.PI_OVER_TWO - Math.acos(Cartesian3.dot(camera.direction, direction)); - expect(camera.tilt).toEqual(tilt); + expect(camera.pitch).toEqual(pitch); }); - it('set tilt throws without angle', function() { - expect(function() { - camera.tilt = undefined; - }).toThrowDeveloperError(); + it('set pitch in CV', function() { + camera._mode = SceneMode.COLUMBUS_VIEW; + + camera.position = Cartesian3.fromDegrees(0.0, 0.0, 100000.0); + camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); + camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); + + var pitch = camera.pitch; + var newPitch = CesiumMath.toRadians(45.0); + camera.setView({ + pitch : newPitch + }); + + expect(camera.pitch).not.toEqual(pitch); + expect(camera.pitch).toEqualEpsilon(newPitch, CesiumMath.EPSILON6); + }); + + it('set pitch in 3D', function() { + camera._mode = SceneMode.SCENE3D; + + camera.position = Cartesian3.clone(Cartesian3.UNIT_X); + camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()); + camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); + + var pitch = camera.pitch; + var newPitch = CesiumMath.toRadians(45.0); + camera.setView({ + pitch : newPitch + }); + + expect(camera.pitch).not.toEqual(pitch); + expect(camera.pitch).toEqualEpsilon(newPitch, CesiumMath.EPSILON14); }); - it('set tilt in CV', function() { + it('roll is undefined when not 3D or Columbus view', function() { + camera._mode = SceneMode.SCENE2D; + expect(camera.roll).not.toBeDefined(); + }); + + it('get roll in CV', function() { camera._mode = SceneMode.COLUMBUS_VIEW; - var tilt = camera.tilt; - var newTilt = CesiumMath.toRadians(45.0); - camera.tilt = newTilt; + camera.position = Cartesian3.fromDegrees(0.0, 0.0, 100000.0); + camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); + camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); + camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); + + camera.look(camera.direction, CesiumMath.toRadians(45.0)); - expect(camera.tilt).not.toEqual(tilt); - expect(camera.tilt).toEqualEpsilon(newTilt, CesiumMath.EPSILON14); + var roll = Math.acos(-camera.right.z) - CesiumMath.PI_OVER_TWO; + expect(camera.roll).toEqualEpsilon(roll, CesiumMath.EPSILON6); }); - it('set tilt in 3D', function() { + it('get roll in 3D', function() { camera._mode = SceneMode.SCENE3D; + var ellipsoid = Ellipsoid.WGS84; camera.position = Cartesian3.clone(Cartesian3.UNIT_X); - camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()); + Cartesian3.multiplyByScalar(camera.position, ellipsoid.maximumRadius + 100.0, camera.position); + camera.direction = new Cartesian3(-1.0, 0.0, 1.0); + Cartesian3.normalize(camera.direction, camera.direction); + camera.right = Cartesian3.cross(camera.direction, Cartesian3.UNIT_Z, new Cartesian3()); + Cartesian3.normalize(camera.right, camera.right); + camera.up = Cartesian3.cross(camera.right, camera.direction, new Cartesian3()); + + var toFixedFrame = Transforms.eastNorthUpToFixedFrame(camera.position, ellipsoid); + var transform = Matrix4.getRotation(toFixedFrame, new Matrix3()); + Matrix3.transpose(transform, transform); + + var right = Matrix3.multiplyByVector(transform, camera.right, new Cartesian3()); + var roll = Math.atan2(right.z, right.x); + + expect(camera.roll).toEqual(roll); + }); + + it('set roll in CV', function() { + camera._mode = SceneMode.COLUMBUS_VIEW; + + camera.position = Cartesian3.fromDegrees(0.0, 0.0, 100000.0); + camera.direction = Cartesian3.negate(Cartesian3.normalize(camera.position, new Cartesian3()), new Cartesian3()); camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); camera.right = Cartesian3.cross(camera.direction, camera.up, new Cartesian3()); - var tilt = camera.tilt; - var newTilt = CesiumMath.toRadians(45.0); - camera.tilt = newTilt; + var roll = camera.roll; + var newRoll = CesiumMath.PI_OVER_FOUR; + camera.setView({ + roll : newRoll + }); + + expect(camera.roll).not.toEqual(roll); + expect(camera.roll).toEqualEpsilon(newRoll, CesiumMath.EPSILON6); + }); + + it('set roll in 3D', function() { + camera._mode = SceneMode.SCENE3D; + + var roll = camera.roll; + var newRoll = CesiumMath.PI_OVER_FOUR; + camera.setView({ + roll : newRoll + }); - expect(camera.tilt).not.toEqual(tilt); - expect(camera.tilt).toEqualEpsilon(newTilt, CesiumMath.EPSILON14); + expect(camera.roll).not.toEqual(roll); + expect(camera.roll).toEqualEpsilon(newRoll, CesiumMath.EPSILON6); }); it('update throws without mode', function() { @@ -278,6 +363,82 @@ defineSuite([ expect(camera.right).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON9); }); + it('setView with cartographic in 2D', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + var maxRadii = ellipsoid.maximumRadius; + + camera._mode = SceneMode.SCENE2D; + camera._projection = projection; + + var frustum = new OrthographicFrustum(); + frustum.right = maxRadii * Math.PI; + frustum.left = -frustum.right; + frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); + frustum.bottom = -frustum.top; + frustum.near = 0.01 * maxRadii; + frustum.far = 60.0 * maxRadii; + camera.frustum = frustum; + + var ratio = frustum.top / frustum.right; + var cart = Cartographic.fromDegrees(-75.0, 42.0, 100.0); + camera.setView({ + heading : 0.0, + pitch : CesiumMath.PI_OVER_TWO, + roll : 0.0, + cartographic : cart + }); + + expect(Cartesian2.fromCartesian3(camera.position, new Cartesian2())).toEqualEpsilon(Cartesian2.fromCartesian3(projection.project(cart), new Cartesian2()), CesiumMath.EPSILON11); + expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON6); + expect(camera.right).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON6); + expect(frustum.right - frustum.left).toEqualEpsilon(cart.height, CesiumMath.EPSILON6); + expect(frustum.top / frustum.right).toEqual(ratio); + }); + + it('setView with cartographic in Columbus View', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + + camera._mode = SceneMode.COLUMBUS_VIEW; + camera._projection = projection; + + var cart = Cartographic.fromDegrees(-75.0, 42.0, 100.0); + camera.setView({ + heading : 0.0, + pitch : CesiumMath.PI_OVER_TWO, + roll : 0.0, + cartographic : cart + }); + + expect(camera.position).toEqualEpsilon(projection.project(cart), CesiumMath.EPSILON11); + expect(camera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Y, CesiumMath.EPSILON6); + expect(camera.right).toEqualEpsilon(Cartesian3.UNIT_X, CesiumMath.EPSILON6); + }); + + it('setView with cartographic in 3D', function() { + var ellipsoid = Ellipsoid.WGS84; + var projection = new GeographicProjection(ellipsoid); + + camera._mode = SceneMode.SCENE3D; + camera._projection = projection; + + var cart = new Cartographic(-75.0, 0.0, 100.0); + camera.setView({ + heading : 0.0, + pitch : CesiumMath.PI_OVER_TWO, + roll : 0.0, + cartographic : cart + }); + + expect(camera.position).toEqualEpsilon(ellipsoid.cartographicToCartesian(cart), CesiumMath.EPSILON6); + expect(camera.direction).toEqualEpsilon(Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON6); + expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON6); + expect(camera.right).toEqualEpsilon(Cartesian3.cross(camera.direction, camera.up, new Cartesian3()), CesiumMath.EPSILON6); + }); + it('worldToCameraCoordinates throws without cartesian', function() { expect(function() { camera.worldToCameraCoordinates(); @@ -1232,72 +1393,6 @@ defineSuite([ expect(p).toBeUndefined(); }); - it('set position cartographic throws without a cartographic', function() { - expect(function() { - camera.setPositionCartographic(); - }).toThrowDeveloperError(); - }); - - it('set position cartographic in 2D', function() { - var ellipsoid = Ellipsoid.WGS84; - var projection = new GeographicProjection(ellipsoid); - var maxRadii = ellipsoid.maximumRadius; - - camera._mode = SceneMode.SCENE2D; - camera._projection = projection; - - var frustum = new OrthographicFrustum(); - frustum.right = maxRadii * Math.PI; - frustum.left = -frustum.right; - frustum.top = frustum.right * (scene.drawingBufferHeight / scene.drawingBufferWidth); - frustum.bottom = -frustum.top; - frustum.near = 0.01 * maxRadii; - frustum.far = 60.0 * maxRadii; - camera.frustum = frustum; - - var ratio = frustum.top / frustum.right; - var cart = new Cartographic(-75.0, 42.0, 100.0); - camera.setPositionCartographic(cart); - - expect(Cartesian2.fromCartesian3(camera.position, new Cartesian2())).toEqual(Cartesian2.fromCartesian3(projection.project(cart), new Cartesian2())); - expect(camera.direction).toEqual(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); - expect(camera.up).toEqual(Cartesian3.UNIT_Y); - expect(camera.right).toEqual(Cartesian3.UNIT_X); - expect(frustum.right - frustum.left).toEqual(cart.height); - expect(frustum.top / frustum.right).toEqual(ratio); - }); - - it('set position cartographic in Columbus View', function() { - var ellipsoid = Ellipsoid.WGS84; - var projection = new GeographicProjection(ellipsoid); - - camera._mode = SceneMode.COLUMBUS_VIEW; - camera._projection = projection; - - var cart = new Cartographic(-75.0, 42.0, 100.0); - camera.setPositionCartographic(cart); - expect(camera.position).toEqual(projection.project(cart)); - expect(camera.direction).toEqual(Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3())); - expect(camera.up).toEqual(Cartesian3.UNIT_Y); - expect(camera.right).toEqual(Cartesian3.UNIT_X); - }); - - it('set position cartographic in 3D', function() { - var ellipsoid = Ellipsoid.WGS84; - var projection = new GeographicProjection(ellipsoid); - - camera._mode = SceneMode.SCENE3D; - camera._projection = projection; - - var cart = new Cartographic(-75.0, 0.0, 100.0); - camera.setPositionCartographic(cart); - - expect(camera.position).toEqual(ellipsoid.cartographicToCartesian(cart)); - expect(camera.direction).toEqual(Cartesian3.normalize(Cartesian3.negate(camera.position, new Cartesian3()), new Cartesian3())); - expect(camera.up).toEqualEpsilon(Cartesian3.UNIT_Z, CesiumMath.EPSILON15); - expect(camera.right).toEqual(Cartesian3.cross(camera.direction, camera.up, new Cartesian3()), new Cartesian3()); - }); - it('get pick ray throws without a position', function() { expect(function () { camera.getPickRay();