|
// Version 0.0.0. Copyright 2017 Mike Bostock. |
|
(function(global, factory) { |
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
|
typeof define === 'function' && define.amd ? define(factory) : |
|
(global.versor = factory()); |
|
}(this, (function() {'use strict'; |
|
|
|
var acos = Math.acos, |
|
asin = Math.asin, |
|
atan2 = Math.atan2, |
|
cos = Math.cos, |
|
max = Math.max, |
|
min = Math.min, |
|
PI = Math.PI, |
|
sin = Math.sin, |
|
sqrt = Math.sqrt, |
|
radians = PI / 180, |
|
degrees = 180 / PI; |
|
|
|
// Returns the unit quaternion for the given Euler rotation angles [λ, φ, γ]. |
|
function versor(e) { |
|
var l = e[0] / 2 * radians, sl = sin(l), cl = cos(l), // λ / 2 |
|
p = e[1] / 2 * radians, sp = sin(p), cp = cos(p), // φ / 2 |
|
g = e[2] / 2 * radians, sg = sin(g), cg = cos(g); // γ / 2 |
|
return [ |
|
cl * cp * cg + sl * sp * sg, |
|
sl * cp * cg - cl * sp * sg, |
|
cl * sp * cg + sl * cp * sg, |
|
cl * cp * sg - sl * sp * cg |
|
]; |
|
} |
|
|
|
// Returns Cartesian coordinates [x, y, z] given spherical coordinates [λ, φ]. |
|
versor.cartesian = function(e) { |
|
var l = e[0] * radians, p = e[1] * radians; |
|
return [cos(p) * cos(l), cos(p) * sin(l), sin(p)]; |
|
}; |
|
|
|
// Returns the Euler rotation angles [λ, φ, γ] for the given quaternion. |
|
versor.rotation = function(q) { |
|
return [ |
|
atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees, |
|
asin(max(-1, min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees, |
|
atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees |
|
]; |
|
}; |
|
|
|
// Returns the quaternion to rotate between two cartesian points on the sphere. |
|
versor.delta = function(v0, v1) { |
|
var w = cross(v0, v1), l = sqrt(dot(w, w)); |
|
if (!l) return [1, 0, 0, 0]; |
|
var t = acos(max(-1, min(1, dot(v0, v1)))) / 2, s = sin(t); // t = θ / 2 |
|
return [cos(t), w[2] / l * s, -w[1] / l * s, w[0] / l * s]; |
|
}; |
|
|
|
// Returns the quaternion that represents q0 * q1. |
|
versor.multiply = function(q0, q1) { |
|
return [ |
|
q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2] - q0[3] * q1[3], |
|
q0[1] * q1[0] + q0[0] * q1[1] + q0[2] * q1[3] - q0[3] * q1[2], |
|
q0[0] * q1[2] - q0[1] * q1[3] + q0[2] * q1[0] + q0[3] * q1[1], |
|
q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1] + q0[3] * q1[0] |
|
]; |
|
}; |
|
|
|
function cross(v0, v1) { |
|
return [ |
|
v0[1] * v1[2] - v0[2] * v1[1], |
|
v0[2] * v1[0] - v0[0] * v1[2], |
|
v0[0] * v1[1] - v0[1] * v1[0] |
|
]; |
|
} |
|
|
|
function dot(v0, v1) { |
|
return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; |
|
} |
|
|
|
return versor; |
|
}))); |