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;