kano-wand
Version:
NodeJS wrapper for interfacing with the Kano Harry Potter BLE wand
166 lines (165 loc) • 5.18 kB
JavaScript
/* global event */
/* eslint no-restricted-globals: ["error"] */
module.exports = class WandConversion {
constructor(x, y) {
this.canvas = {
x,
y,
};
this.matrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
this.zeroVector = { x: 0, y: 0, z: 0 };
this.oneVector = { x: 1, y: 1, z: 1 };
this.euler = {
x: 0, y: 0, z: 0,
};
}
position(quat) {
const quaternion = quat.map(v => v / 1024);
const normQuaternion = WandConversion.normalize(quaternion);
const matrixRotatedFromQuat = WandConversion.makeRotationFromQuaternion(
this.zeroVector,
normQuaternion,
this.oneVector,
this.matrix
);
const [yaw, roll, pitch] = WandConversion.setFromRotationMatrix(matrixRotatedFromQuat);
const yawComplete = WandConversion.yawComplete(yaw, pitch);
const [x, y] = this.getXY(yaw, pitch);
return {
x,
y,
pitch: pitch / 2,
roll: -roll,
yaw: yawComplete,
};
}
getXY(y, p) {
const w = this.canvas.x / 2;
const h = this.canvas.y / 2;
return [-(((y / 180) * (w * 4)) - w), h - (3 * p)];
}
// from Three.js https://github.com/mrdoob/three.js/blob/master/src/math/Quaternion.js
static normalize(quat) {
let length = WandConversion.quatLength(quat);
let x = quat[0];
let y = quat[1];
let z = quat[2];
let w = quat[3];
if (length === 0) {
x = 0;
y = 0;
z = 0;
w = 1;
} else {
length = 1 / length;
x *= length;
y *= length;
z *= length;
w *= length;
}
return {
x, y, z, w,
};
}
// from Three.js https://github.com/mrdoob/three.js/blob/master/src/math/Quaternion.js
static quatLength(quat) {
return Math.sqrt((quat[1] ** 2) + (quat[2] ** 2) + (quat[3] ** 2) + (quat[0] ** 2));
}
// from Three.js https://github.com/mrdoob/three.js/blob/master/src/math/Euler.js
static setFromRotationMatrix(matrix) {
const te = matrix;
const matrix11 = te[0];
const matrix12 = te[4];
const matrix13 = te[8];
const matrix22 = te[5];
const matrix23 = te[9];
const matrix32 = te[6];
const matrix33 = te[10];
const y = Math.asin(WandConversion.clamp(matrix13, -1, 1));
let x;
let z;
if (Math.abs(matrix13) < 0.99999) {
x = Math.atan2(-matrix23, matrix33);
z = Math.atan2(-matrix12, matrix11);
} else {
x = Math.atan2(matrix32, matrix22);
z = 0;
}
return [y, z, x * 2].map(WandConversion.toDegrees).map(WandConversion.toReal);
}
// from Three.js https://github.com/mrdoob/three.js/blob/master/src/math/Matrix4.js
static makeRotationFromQuaternion(position, quaternion, scale, matrix) {
const te = matrix;
const {
x, y, z, w,
} = quaternion;
const xx = x * (x + x);
const xy = x * (y + y);
const xz = x * (z + z);
const yy = y * (y + y);
const yz = y * (z + z);
const zz = z * (z + z);
const wx = w * (x + x);
const wy = w * (y + y);
const wz = w * (z + z);
const sx = scale.x;
const sy = scale.y;
const sz = scale.z;
te[0] = (1 - (yy + zz)) * sx;
te[1] = (xy + wz) * sx;
te[2] = (xz - wy) * sx;
te[3] = 0;
te[4] = (xy - wz) * sy;
te[5] = (1 - (xx + zz)) * sy;
te[6] = (yz + wx) * sy;
te[7] = 0;
te[8] = (xz + wy) * sz;
te[9] = (yz - wx) * sz;
te[10] = (1 - (xx + yy)) * sz;
te[11] = 0;
te[12] = position.x;
te[13] = position.y;
te[14] = position.z;
te[15] = 1;
return te;
}
// from Three.js https://github.com/mrdoob/three.js/blob/master/src/math/Math.js
static clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
static yawComplete(yaw, pitch) {
let yawComplete;
if (pitch > 180 || pitch < -180) {
yawComplete = 180 - (-yaw);
} else {
yawComplete = -yaw;
}
return yawComplete;
}
static toDegrees(angle) {
return angle * (180 / Math.PI);
}
static toRadian(angle) {
return angle * (Math.PI / 180);
}
static toReal(x) {
if (!isNaN(parseFloat(x)) && isFinite(parseFloat(x))) {
return parseFloat(parseFloat(x).toFixed(1));
}
return x;
}
static map(n, start1, stop1, start2, stop2) {
return (((n - start1) / (stop1 - start1)) * (stop2 - start2)) + start2;
}
static arrMultiply(array, num1, num2) {
return array[num1] * array[num2];
}
static distance(x1, x2, y1, y2) {
return Math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2));
}
};