var Point3d = require('./Point3d');
|
|
|
|
/**
|
|
* @class Camera
|
|
* The camera is mounted on a (virtual) camera arm. The camera arm can rotate
|
|
* The camera is always looking in the direction of the origin of the arm.
|
|
* This way, the camera always rotates around one fixed point, the location
|
|
* of the camera arm.
|
|
*
|
|
* Documentation:
|
|
* http://en.wikipedia.org/wiki/3D_projection
|
|
*/
|
|
function Camera() {
|
|
this.armLocation = new Point3d();
|
|
this.armRotation = {};
|
|
this.armRotation.horizontal = 0;
|
|
this.armRotation.vertical = 0;
|
|
this.armLength = 1.7;
|
|
this.cameraOffset = new Point3d();
|
|
this.offsetMultiplier = 0.6;
|
|
|
|
this.cameraLocation = new Point3d();
|
|
this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0);
|
|
|
|
this.calculateCameraOrientation();
|
|
}
|
|
|
|
/**
|
|
* Set offset camera in camera coordinates
|
|
* @param {Number} x offset by camera horisontal
|
|
* @param {Number} y offset by camera vertical
|
|
*/
|
|
Camera.prototype.setOffset = function(x, y) {
|
|
var abs = Math.abs,
|
|
sign = Math.sign,
|
|
mul = this.offsetMultiplier,
|
|
border = this.armLength * mul;
|
|
|
|
if (abs(x) > border) {
|
|
x = sign(x) * border;
|
|
}
|
|
if (abs(y) > border) {
|
|
y = sign(y) * border;
|
|
}
|
|
this.cameraOffset.x = x;
|
|
this.cameraOffset.y = y;
|
|
this.calculateCameraOrientation();
|
|
};
|
|
|
|
|
|
/**
|
|
* Get camera offset by horizontal and vertical
|
|
*/
|
|
Camera.prototype.getOffset = function() {
|
|
return this.cameraOffset;
|
|
};
|
|
|
|
/**
|
|
* Set the location (origin) of the arm
|
|
* @param {Number} x Normalized value of x
|
|
* @param {Number} y Normalized value of y
|
|
* @param {Number} z Normalized value of z
|
|
*/
|
|
Camera.prototype.setArmLocation = function(x, y, z) {
|
|
this.armLocation.x = x;
|
|
this.armLocation.y = y;
|
|
this.armLocation.z = z;
|
|
|
|
this.calculateCameraOrientation();
|
|
};
|
|
|
|
/**
|
|
* Set the rotation of the camera arm
|
|
* @param {Number} horizontal The horizontal rotation, between 0 and 2*PI.
|
|
* Optional, can be left undefined.
|
|
* @param {Number} vertical The vertical rotation, between 0 and 0.5*PI
|
|
* if vertical=0.5*PI, the graph is shown from the
|
|
* top. Optional, can be left undefined.
|
|
*/
|
|
Camera.prototype.setArmRotation = function(horizontal, vertical) {
|
|
if (horizontal !== undefined) {
|
|
this.armRotation.horizontal = horizontal;
|
|
}
|
|
|
|
if (vertical !== undefined) {
|
|
this.armRotation.vertical = vertical;
|
|
if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
|
|
if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI;
|
|
}
|
|
|
|
if (horizontal !== undefined || vertical !== undefined) {
|
|
this.calculateCameraOrientation();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieve the current arm rotation
|
|
* @return {object} An object with parameters horizontal and vertical
|
|
*/
|
|
Camera.prototype.getArmRotation = function() {
|
|
var rot = {};
|
|
rot.horizontal = this.armRotation.horizontal;
|
|
rot.vertical = this.armRotation.vertical;
|
|
|
|
return rot;
|
|
};
|
|
|
|
/**
|
|
* Set the (normalized) length of the camera arm.
|
|
* @param {Number} length A length between 0.71 and 5.0
|
|
*/
|
|
Camera.prototype.setArmLength = function(length) {
|
|
if (length === undefined)
|
|
return;
|
|
|
|
this.armLength = length;
|
|
|
|
// Radius must be larger than the corner of the graph,
|
|
// which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
|
|
// graph
|
|
if (this.armLength < 0.71) this.armLength = 0.71;
|
|
if (this.armLength > 5.0) this.armLength = 5.0;
|
|
|
|
this.setOffset(this.cameraOffset.x, this.cameraOffset.y);
|
|
this.calculateCameraOrientation();
|
|
};
|
|
|
|
/**
|
|
* Retrieve the arm length
|
|
* @return {Number} length
|
|
*/
|
|
Camera.prototype.getArmLength = function() {
|
|
return this.armLength;
|
|
};
|
|
|
|
/**
|
|
* Retrieve the camera location
|
|
* @return {Point3d} cameraLocation
|
|
*/
|
|
Camera.prototype.getCameraLocation = function() {
|
|
return this.cameraLocation;
|
|
};
|
|
|
|
/**
|
|
* Retrieve the camera rotation
|
|
* @return {Point3d} cameraRotation
|
|
*/
|
|
Camera.prototype.getCameraRotation = function() {
|
|
return this.cameraRotation;
|
|
};
|
|
|
|
/**
|
|
* Calculate the location and rotation of the camera based on the
|
|
* position and orientation of the camera arm
|
|
*/
|
|
Camera.prototype.calculateCameraOrientation = function() {
|
|
// calculate location of the camera
|
|
this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
|
|
this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
|
|
this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
|
|
|
|
// calculate rotation of the camera
|
|
this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical;
|
|
this.cameraRotation.y = 0;
|
|
this.cameraRotation.z = -this.armRotation.horizontal;
|
|
|
|
var xa = this.cameraRotation.x;
|
|
var za = this.cameraRotation.z;
|
|
var dx = this.cameraOffset.x;
|
|
var dy = this.cameraOffset.y;
|
|
var sin = Math.sin, cos = Math.cos;
|
|
|
|
this.cameraLocation.x = this.cameraLocation.x + dx * cos(za) + dy * - sin(za) * cos(xa);
|
|
this.cameraLocation.y = this.cameraLocation.y + dx * sin(za) + dy * cos(za) * cos(xa);
|
|
this.cameraLocation.z = this.cameraLocation.z + dy * sin(xa);
|
|
};
|
|
|
|
module.exports = Camera;
|