/*
* Copyright 2003-2006, 2009, 2017, 2018, 2019, United States Government, as represented by the Administrator of the
* National Aeronautics and Space Administration. All rights reserved.
*
* The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @exports ArcBallCamera
*/
define([
'./geom/Angle',
'./error/ArgumentError',
'./util/Logger',
'./geom/Matrix',
'./geom/Position',
'./geom/Vec3',
'./util/WWMath',
],
function (
Angle,
ArgumentError,
Logger,
Matrix,
Position,
Vec3,
WWMath) {
'use strict';
/**
* Constructs an arc ball camera.
* @alias ArcBallCamera
* @constructor
* @classdesc Represents an arc ball camera.
* @param {WorldWindow} wwd The WorldWindow instance.
* @throws {ArgumentError} If the WorldWindow instance is missing.
*/
var ArcBallCamera = function (wwd) {
if (!wwd) {
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ArcBallCamera", "constructor",
"missing WorldWindow instance."));
}
/**
* The position to look at
* @type {Position}
*/
this.position = new Position(30, -110, 0);
/**
* The heading of the camera, between -180 to +180 degrees.
* It is used to rotate left or right around the look at position.
* @type {Number}
* @default 0
*/
this.heading = 0;
/**
* The tilt of the camera, between 0 to +90 degrees.
* It is used to rotate up or down around the look at position.
* @type {Number}
* @default 0
*/
this.tilt = 0;
/**
* The range from the camera to the look at position.
* It is used to zoom in or out.
* @type {Number}
* @default 10000000
*/
this.range = 10000000;
/**
* This camera's roll, in degrees.
* @type {Number}
* @default 0
*/
this.roll = 0;
/**
* The WorldWindow instance.
* @type {WorldWindow}
*/
this.wwd = wwd;
//Internal variables for this class.
this._scratchMatrix = Matrix.fromIdentity();
this._scratchVector = new Vec3(0, 0, 0);
};
/**
* Creates a view matrix for this camera.
* @param {Matrix} matrix A matrix in which to set the view matrix.
* @returns {Matrix} The view matrix.
* @throws {ArgumentError} If the specified matrix is missing.
*/
ArcBallCamera.prototype.createViewMatrix = function (matrix) {
if (!matrix) {
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ArcBallCamera", "createViewMatrix",
"missing matrix"));
}
this.applyLimits();
matrix.setToIdentity();
matrix.multiplyByLookAtModelview(this.position, this.range, this.heading, this.tilt, this.roll, this.wwd.globe);
return matrix;
};
/**
* Clones this camera to the specified camera.
* If a camera is not provied a new one will be created.
* @param {ArcBallCamera | undefined} camera The camera to save the rsults in.
* @returns {ArcBallCamera} A colne of this camera.
*/
ArcBallCamera.prototype.clone = function (camera) {
camera = camera || new ArcBallCamera(this.wwd);
camera.copy(this);
return camera;
};
/**
* Copies this camera to the specified camera.
* @param {ArcBallCamera} camera The camera to copy.
* @returns {ArcBallCamera} This camera, set to the values of the specified camera.
* @throws {ArgumentError} If the specified camera is missing.
*/
ArcBallCamera.prototype.copy = function (camera) {
if (!camera) {
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ArcBallCamera", "copy",
"missing camera"));
}
this.position.copy(camera.position);
this.tilt = camera.tilt;
this.heading = camera.heading;
this.range = camera.range;
this.roll = camera.roll;
return this;
};
/**
* Enforces navigation limits for this camera:
* - a valid position: latitude -90 to 90 deg, longitude -180 to 180 deg, altitude, 0 to Number.MAX_VALUE
* - a valid heading angle: -180 to 180 deg
* - a valid tilt angle: 0 to 90 deg, 0 deg for 2D projections
* - a valid roll angle: -180 to 180 deg
* - a valid range: 1 to Number.MAX_VALUE, 1 to 40075016 for 2D projections
*/
ArcBallCamera.prototype.applyLimits = function () {
// Clamp latitude to between -90 and +90, and normalize longitude to between -180 and +180.
this.position.latitude = WWMath.clamp(this.position.latitude, -90, 90);
this.position.longitude = Angle.normalizedDegreesLongitude(this.position.longitude);
// Clamp altitude to values greater than 0 in order to prevent looking undergound.
this.position.altitude = WWMath.clamp(this.position.altitude, 0, Number.MAX_VALUE);
// Clamp range to values greater than 1 in order to prevent degenerating to a first-person navigator when
// range is zero.
this.range = WWMath.clamp(this.range, 1, Number.MAX_VALUE);
// Normalize heading to between -180 and +180.
this.heading = Angle.normalizedDegrees(this.heading);
// Clamp tilt to between 0 and +90 to prevent the viewer from going upside down.
this.tilt = WWMath.clamp(this.tilt, 0, 90);
// Normalize roll to between -180 and +180.
this.roll = Angle.normalizedDegrees(this.roll);
// Apply 2D limits when the globe is 2D.
if (this.wwd.globe.is2D()) {
// Clamp range to prevent more than 360 degrees of visible longitude. Assumes a 45 degree horizontal
// field of view.
var maxRange = 2 * Math.PI * this.wwd.globe.equatorialRadius;
this.range = WWMath.clamp(this.range, 1, maxRange);
// Force tilt to 0 when in 2D mode to keep the viewer looking straight down.
this.tilt = 0;
}
};
/**
* Converts this arc ball camera to a first person camera.
* @param {FirstPersonCamera} camera A camera in which to save the result of the transformation.
* @returns {FirstPersonCamera} The specified camera.
* @throws {ArgumentError} If the specified camera is missing.
*/
ArcBallCamera.prototype.toFirstPerson = function (camera) {
if (!camera) {
throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ArcBallCamera", "toFirstPerson",
"missing camera"));
}
var viewMatrix = this.createViewMatrix(this._scratchMatrix);
var eyePoint = viewMatrix.extractEyePoint(this._scratchVector);
var params = viewMatrix.extractViewingParameters(eyePoint, this.roll, this.wwd.globe, {});
camera.position.copy(params.origin);
camera.heading = params.heading;
camera.tilt = params.tilt - 90;
camera.roll = params.roll;
camera.applyLimits();
return camera;
};
return ArcBallCamera;
});