src/node-coordinateCalculator.js
// SAGE2 is available for use under the SAGE2 Software License
//
// University of Illinois at Chicago's Electronic Visualization Laboratory (EVL)
// and University of Hawai'i at Manoa's Laboratory for Advanced Visualization and
// Applications (LAVA)
//
// See full text, terms and conditions in the LICENSE.txt included file
//
// Copyright (c) 2014
/**
* Calculate intersection of ray and cylinder for CAVE2
*
* @module server
* @submodule coordinateCalculator
*/
// require variables to be declared
"use strict";
var max_y_error = 0.5; // meters
var max_x_error = 0.005; // fractional
var radius = 6.477 / 2;
var radiansForDoor = 36 * Math.PI / 180;
var minY = 0.305;
var maxY = 2.625;
var position = {};
var eulerAngles = {};
var screenPos = {};
/**
* CoordinateCalculator class
*
* @class CoordinateCalculator
* @constructor
*/
function CoordinateCalculator() {
position.x = 0;
position.y = 0;
position.z = 1;
eulerAngles.x = 0;
eulerAngles.y = 0;
eulerAngles.z = 1;
screenPos.x = 0.5;
screenPos.y = 0.5;
}
/**
* Calculate wand to screen intersection
*
* @method wandToScreenCoordinates
* @param x {Number} wand x
* @param y {Number} wand x
* @param z {Number} wand x
* @param rx {Number} ray x
* @param ry {Number} ray y
* @param rz {Number} ray z
* @param rw {Number} ray w
* @return {Object} screen coordinates .x and .y
*/
CoordinateCalculator.prototype.wandToScreenCoordinates = function(x, y, z, rx, ry, rz, rw) {
// Quaternion to Euler ////////////////////////
// Rotation matrix Q multiplied by reference vector (0,0,-1)
// | 1 - 2y^2 - 2z^2 , 2xy - 2zw, 2xz + 2yw | |0 |
// Q = | 2xy + 2zw, 1 - 2x^2 - 2z^2, 2yz - 2xw | * |0 |
// | 2xz - 2yw, 2yz + 2xw, 1 - 2x^2 - 2y^2 | |-1|
eulerAngles.x = -1 * (2 * rx * rz + 2 * ry * rw);
eulerAngles.y = -1 * (2 * ry * rz - 2 * rx * rw);
eulerAngles.z = -1 * (1 - 2 * (rx * rx) - 2 * (ry * ry));
if (rx * ry + rz * rw === 0.5) {
// North pole
eulerAngles.x = 2 * Math.atan2(rx, rw);
eulerAngles.z = 0;
} else if (rx * ry + rz * rw === -0.5) {
// South pole
eulerAngles.x = -2 * Math.atan2(rx, rw);
eulerAngles.z = 0;
}
// QuaternionToEuler ends ///////////////////
// Orientation is vertical
if (eulerAngles.x === 0 && eulerAngles.z === 0) {
screenPos.x = -1;
screenPos.y = -1;
} else {
var h = 0; // x-coordinate of the center of the circle
var k = 0; // z-coordinate of the center of the circle
var ox = eulerAngles.x; // parametric slope of x, from orientation vector
var oy = eulerAngles.y; // parametric slope of y, from orientation vector
var oz = eulerAngles.z; // parametric slope of z, from orientation vector
var r = radius; // radius of cylinder
// A * t^2 + B * t + C
var A = ox * ox + oz * oz;
var B = 2 * ox * x + 2 * oz * z - 2 * h * ox - 2 * k * oz;
var C = x * x + z * z + h * h + k * k - r * r - 2 * h * x - 2 * k * z;
if (A !== 0 && (B * B - 4 * A * C) >= 0) {
var t1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A);
var t2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
var t = 0;
if (t1 >= 0) {
t = t1;
} else if (t2 >= 0) {
t = t2;
} else {
screenPos.x = -1;
screenPos.y = -1;
}
var x_pos = ox * t + x;
var y_pos = oy * t + y;
var z_pos = oz * t + z;
this.calculateScreenPos(x_pos, y_pos, z_pos);
} else {
screenPos.x = -1;
screenPos.y = -1;
}
}
return screenPos;
};
/**
* Calculate screen coordinates
*
* @method calculateScreenPos
* @param x {Number} wand x
* @param y {Number} wand x
* @param z {Number} wand x
*/
CoordinateCalculator.prototype.calculateScreenPos = function(x, y, z) {
if (y > maxY) {
if (y < maxY + max_y_error) {
y = maxY;
} else {
screenPos.x = -1;
screenPos.y = -1;
return;
}
}
if (y < minY) {
if (y > minY + max_y_error) {
y = minY;
} else {
screenPos.x = -1;
screenPos.y = -1;
return;
}
}
var angle = Math.atan2(x, z);
if (angle < 0) {
angle += 2 * Math.PI;
}
angle = 2 * Math.PI - angle;
angle -= radiansForDoor / 2;
x = angle / (2 * Math.PI - radiansForDoor);
x += 0.02777777777;
if (x > 1) {
if (x < 1 + max_x_error) {
x = 1;
} else {
screenPos.x = -1;
screenPos.y = -1;
return;
}
}
if (x < 0) {
if (x > -max_x_error) {
x = 0;
} else {
screenPos.x = -1;
screenPos.y = -1;
return;
}
}
y -= minY;
y /= (maxY - minY);
screenPos.x = x;
screenPos.y = 1 - y;
};
module.exports = CoordinateCalculator;