content/hifi-content/luis/VectorMath.js
2022-02-14 02:04:11 +01:00

139 lines
No EOL
5.2 KiB
JavaScript

//
// Created by Luis Cuenca on 1/31/18
// Copyright 2018 High Fidelity, Inc.
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* jslint bitwise: true */
/* global Script
*/
(function(){
var Vector3 = new (function() {
var self = this;
this.EPSILON = 0.000001;
this.EPSILON_SQUARED = self.EPSILON * self.EPSILON;
this.PI = 3.14159265358979;
this.ALMOST_ONE= 1.0 - self.EPSILON;
this.PI_OVER_TWO = 1.57079632679490;
this.cross = function(A, B) {
return {x: (A.y * B.z - A.z * B.y), y: (A.z * B.x - A.x * B.z), z: (A.x * B.y - A.y * B.x)};
};
this.distance = function(A, B) {
return Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + (A.z - B.z) * (A.z - B.z));
};
this.dot = function(A, B) {
return A.x * B.x + A.y * B.y + A.z * B.z;
};
this.length = function(V) {
return Math.sqrt(V.x * V.x + V.y * V.y + V.z * V.z);
};
this.subtract = function(A, B) {
return {x: (A.x - B.x), y: (A.y - B.y), z: (A.z - B.z)};
};
this.sum = function(A, B) {
return {x: (A.x + B.x), y: (A.y + B.y), z: (A.z + B.z)};
};
this.multiply = function(V, scale) {
return {x: scale * V.x, y: scale * V.y, z: scale * V.z};
};
this.normalize = function(V) {
var L2 = V.x*V.x + V.y*V.y + V.z*V.z;
if (L2 < self.EPSILON_SQUARED) {
return {x: V.x, y: V.y, z: V.z};
}
var invL = 1.0/Math.sqrt(L2);
return {x: invL * V.x, y: invL * V.y, z: invL * V.z};
};
this.multiplyQbyV = function(Q,V) {
var num = Q.x * 2.0;
var num2 = Q.y * 2.0;
var num3 = Q.z * 2.0;
var num4 = Q.x * num;
var num5 = Q.y * num2;
var num6 = Q.z * num3;
var num7 = Q.x * num2;
var num8 = Q.x * num3;
var num9 = Q.y * num3;
var num10 = Q.w * num;
var num11 = Q.w * num2;
var num12 = Q.w * num3;
var result = {x: 0, y: 0, z: 0};
result.x = (1.0 - (num5 + num6)) * V.x + (num7 - num12) * V.y + (num8 + num11) * V.z;
result.y = (num7 + num12) * V.x + (1.0 - (num4 + num6)) * V.y + (num9 - num10) * V.z;
result.z = (num8 - num11) * V.x + (num9 + num10) * V.y + (1.0 - (num4 + num5)) * V.z;
return result;
};
})();
var Quaternion = new (function() {
var self = this;
this.IDENTITY = function() {
return {x:0, y:0, z:0, w:1};
};
this.multiply = function(Q, R) {
// from this page:
// http://mathworld.wolfram.com/Quaternion.html
return {
w: Q.w * R.w - Q.x * R.x - Q.y * R.y - Q.z * R.z,
x: Q.w * R.x + Q.x * R.w + Q.y * R.z - Q.z * R.y,
y: Q.w * R.y - Q.x * R.z + Q.y * R.w + Q.z * R.x,
z: Q.w * R.z + Q.x * R.y - Q.y * R.x + Q.z * R.w};
};
this.angleAxis = function(angle, axis) {
var s = Math.sin(0.5 * angle);
return {w: Math.cos(0.5 * angle),x: s * axis.x, y: s * axis.y, z: s * axis.z};
};
this.inverse = function(Q) {
return {w: -Q.w, x: Q.x, y: Q.y, z: Q.z};
};
this.rotationBetween = function(orig, dest) {
var v1 = Vector3.normalize(orig);
var v2 = Vector3.normalize(dest);
var cosTheta = Vector3.dot(v1, v2);
var rotationAxis;
if(cosTheta >= 1 - Vector3.EPSILON){
return self.IDENTITY();
}
if(cosTheta < -1 + Vector3.EPSILON)
{
// special case when vectors in opposite directions :
// there is no "ideal" rotation axis
// So guess one; any will do as long as it's perpendicular to start
// This implementation favors a rotation around the Up axis (Y),
// since it's often what you want to do.
rotationAxis = Vector3.cross({x: 0, y: 0, z: 1}, v1);
if(Vector3.length(rotationAxis) < Vector3.EPSILON) { // bad luck, they were parallel, try again!
rotationAxis = Vector3.cross({x:1, y:0, z:0}, v1);
}
rotationAxis = Vector3.normalize(rotationAxis);
return self.angleAxis(Vector3.PI, rotationAxis);
}
// Implementation from Stan Melax's Game Programming Gems 1 article
rotationAxis = Vector3.cross(v1, v2);
var s = Math.sqrt((1 + cosTheta) * 2);
var invs = 1 / s;
return {
w: s * 0.5,
x: rotationAxis.x * invs,
y: rotationAxis.y * invs,
z: rotationAxis.z * invs,
}
}
})();
Script.registerValue("VEC3", Vector3);
Script.registerValue("QUAT", Quaternion);
})();