UNPKG

@egjs/view360

Version:

360 integrated viewing solution from inside-out view to outside-in view. It provides user-friendly service by rotating 360 degrees through various user interaction such as motion sensor and touch.

1,800 lines (1,494 loc) 208 kB
import PosePredictor from 'webvr-polyfill/src/sensor-fusion/pose-predictor'; import Util from 'webvr-polyfill/src/util'; import MathUtil from 'webvr-polyfill/src/math-util'; import ComplementaryFilter from 'webvr-polyfill/src/sensor-fusion/complementary-filter'; import _ESPromise from 'es6-promise'; import Agent from '@egjs/agent'; import Axes, { PanInput, PinchInput, MoveKeyInput, WheelInput } from '@egjs/axes'; import Component from '@egjs/component'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } /** * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ /* eslint-disable no-new-func, no-nested-ternary */ var win = typeof window !== "undefined" && window.Math === Math ? window : typeof self !== "undefined" && self.Math === Math ? self : Function("return this")(); /* eslint-enable no-new-func, no-nested-ternary */ var doc = win.document; var userAgent = win.navigator.userAgent; var IS_SAFARI_ON_DESKTOP = userAgent.indexOf("Safari") !== -1 && userAgent.indexOf("Chrome") === -1 && userAgent.indexOf("Mac OS X") !== -1; /** * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ win.Float32Array = typeof win.Float32Array !== "undefined" ? win.Float32Array : win.Array; var Float32Array$1 = win.Float32Array; var getComputedStyle = win.getComputedStyle; var userAgent$1 = win.navigator.userAgent; var SUPPORT_TOUCH = "ontouchstart" in win; var SUPPORT_DEVICEMOTION = "ondevicemotion" in win; var DeviceMotionEvent = win.DeviceMotionEvent; var devicePixelRatio = win.devicePixelRatio; var TRANSFORM = function () { var docStyle = doc.documentElement.style; var target = ["transform", "webkitTransform", "msTransform", "mozTransform"]; for (var i = 0, len = target.length; i < len; i++) { if (target[i] in docStyle) { return target[i]; } } return ""; }(); // check for will-change support var SUPPORT_WILLCHANGE = win.CSS && win.CSS.supports && win.CSS.supports("will-change", "transform"); function toAxis(source, offset) { return offset.reduce(function (acc, v, i) { if (source[i]) { acc[source[i]] = v; } return acc; }, {}); } /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix/common.js * Common utilities * modified by egjs */ var glMatrix = {}; glMatrix.ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array; var degree = Math.PI / 180; glMatrix.toRadian = function (a) { return a * degree; }; glMatrix.toDegree = function (a) { return a / degree; }; // glMatrix.EPSILON = 0.000001; glMatrix.EPSILON = 0.0001; /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix/vec3.js * 3 Dimensional Vector Util * modified by egjs */ /** * @class 3 Dimensional Vector * @name vec3 */ var vec3 = {}; /** * Creates a new, empty vec3 * * @returns {vec3} a new 3D vector */ vec3.create = function () { var out = new glMatrix.ARRAY_TYPE(3); out[0] = 0; out[1] = 0; out[2] = 0; return out; }; /** * Creates a new vec3 initialized with the given values * * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @returns {vec3} a new 3D vector */ vec3.fromValues = function (x, y, z) { var out = new glMatrix.ARRAY_TYPE(3); out[0] = x; out[1] = y; out[2] = z; return out; }; vec3.set = function (out, x, y, z) { out[0] = x; out[1] = y; out[2] = z; return out; }; vec3.copy = function (out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; return out; }; /** * Scales a vec3 by a scalar number * * @param {vec3} out the receiving vector * @param {vec3} a the vector to scale * @param {Number} b amount to scale the vector by * @returns {vec3} out */ vec3.scale = function (out, a, b) { out[0] = a[0] * b; out[1] = a[1] * b; out[2] = a[2] * b; return out; }; /** * Subtracts vector b from vector a * * @param {vec3} out the receiving vector * @param {vec3} a the first operand * @param {vec3} b the second operand * @returns {vec3} out */ vec3.subtract = function (out, a, b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; return out; }; /** * Calculates the length of a vec3 * * @param {vec3} a vector to calculate length of * @returns {Number} length of a */ vec3.length = function (a) { var x = a[0], y = a[1], z = a[2]; return Math.sqrt(x * x + y * y + z * z); }; /** * Normalize a vec3 * * @param {vec3} out the receiving vector * @param {vec3} a vector to normalize * @returns {vec3} out */ vec3.normalize = function (out, a) { var x = a[0], y = a[1], z = a[2]; var len = x * x + y * y + z * z; if (len > 0) { //TODO: evaluate use of glm_invsqrt here? len = 1 / Math.sqrt(len); out[0] = a[0] * len; out[1] = a[1] * len; out[2] = a[2] * len; } return out; }; /** * Calculates the dot product of two vec3's * * @param {vec3} a the first operand * @param {vec3} b the second operand * @returns {Number} dot product of a and b */ vec3.dot = function (a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }; /** * Computes the cross product of two vec3's * * @param {vec3} out the receiving vector * @param {vec3} a the first operand * @param {vec3} b the second operand * @returns {vec3} out */ vec3.cross = function (out, a, b) { var ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2]; out[0] = ay * bz - az * by; out[1] = az * bx - ax * bz; out[2] = ax * by - ay * bx; return out; }; /** * Transforms the vec3 with a quat * * @param {vec3} out the receiving vector * @param {vec3} a the vector to transform * @param {quat} q quaternion to transform with * @returns {vec3} out */ vec3.transformQuat = function (out, a, q) { // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations var x = a[0], y = a[1], z = a[2], qx = q[0], qy = q[1], qz = q[2], qw = q[3], // calculate quat * vec ix = qw * x + qy * z - qz * y, iy = qw * y + qz * x - qx * z, iz = qw * z + qx * y - qy * x, iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; return out; }; /** * Rotate a 3D vector around the z-axis * @param {vec3} out The receiving vec3 * @param {vec3} a The vec3 point to rotate * @param {vec3} b The origin of the rotation * @param {Number} c The angle of rotation * @returns {vec3} out */ vec3.rotateZ = function (out, a, b, c) { var p = [], r = []; //Translate point to the origin p[0] = a[0] - b[0]; p[1] = a[1] - b[1]; p[2] = a[2] - b[2]; //perform rotation r[0] = p[0] * Math.cos(c) - p[1] * Math.sin(c); r[1] = p[0] * Math.sin(c) + p[1] * Math.cos(c); r[2] = p[2]; //translate to correct position out[0] = r[0] + b[0]; out[1] = r[1] + b[1]; out[2] = r[2] + b[2]; return out; }; /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix/vec2.js * 2 Dimensional Vector Util * modified by egjs */ /** * @class 2 Dimensional Vector * @name vec2 */ var vec2 = {}; vec2.copy = function (out, a) { out[0] = a[0]; out[1] = a[1]; return out; }; /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix/quat.js * Quaternion util * modified by egjs */ /** * @class Quaternion * @name quat */ var quat = {}; /** * Creates a new identity quat * * @returns {quat} a new quaternion */ quat.create = function () { var out = new glMatrix.ARRAY_TYPE(4); out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 1; return out; }; /** * Creates a new quat initialized with values from an existing quaternion * * @param {quat} a quaternion to clone * @returns {quat} a new quaternion * @function */ quat.clone = function (a) { var out = new glMatrix.ARRAY_TYPE(4); out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; return out; }; /** * Creates a new quat initialized with the given values * * @param {Number} x X component * @param {Number} y Y component * @param {Number} z Z component * @param {Number} w W component * @returns {quat} a new quaternion * @function */ quat.fromValues = function (x, y, z, w) { var out = new glMatrix.ARRAY_TYPE(4); out[0] = x; out[1] = y; out[2] = z; out[3] = w; return out; }; /** * Copy the values from one quat to another * * @param {quat} out the receiving quaternion * @param {quat} a the source quaternion * @returns {quat} out * @function */ quat.copy = function (out, a) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; return out; }; /** * Sets a quat from the given angle and rotation axis, * then returns it. * * @param {quat} out the receiving quaternion * @param {vec3} axis the axis around which to rotate * @param {Number} rad the angle in radians * @returns {quat} out **/ quat.setAxisAngle = function (out, axis, rad) { rad = rad * 0.5; var s = Math.sin(rad); out[0] = s * axis[0]; out[1] = s * axis[1]; out[2] = s * axis[2]; out[3] = Math.cos(rad); return out; }; /** * Multiplies two quat's * * @param {quat} out the receiving quaternion * @param {quat} a the first operand * @param {quat} b the second operand * @returns {quat} out */ quat.multiply = function (out, a, b) { var ax = a[0], ay = a[1], az = a[2], aw = a[3], bx = b[0], by = b[1], bz = b[2], bw = b[3]; out[0] = ax * bw + aw * bx + ay * bz - az * by; out[1] = ay * bw + aw * by + az * bx - ax * bz; out[2] = az * bw + aw * bz + ax * by - ay * bx; out[3] = aw * bw - ax * bx - ay * by - az * bz; return out; }; /** * Rotates a quaternion by the given angle about the X axis * * @param {quat} out quat receiving operation result * @param {quat} a quat to rotate * @param {number} rad angle (in radians) to rotate * @returns {quat} out */ quat.rotateX = function (out, a, rad) { rad *= 0.5; var ax = a[0], ay = a[1], az = a[2], aw = a[3], bx = Math.sin(rad), bw = Math.cos(rad); out[0] = ax * bw + aw * bx; out[1] = ay * bw + az * bx; out[2] = az * bw - ay * bx; out[3] = aw * bw - ax * bx; return out; }; /** * Rotates a quaternion by the given angle about the Y axis * * @param {quat} out quat receiving operation result * @param {quat} a quat to rotate * @param {number} rad angle (in radians) to rotate * @returns {quat} out */ quat.rotateY = function (out, a, rad) { rad *= 0.5; var ax = a[0], ay = a[1], az = a[2], aw = a[3], by = Math.sin(rad), bw = Math.cos(rad); out[0] = ax * bw - az * by; out[1] = ay * bw + aw * by; out[2] = az * bw + ax * by; out[3] = aw * bw - ay * by; return out; }; /** * Calculates the conjugate of a quat * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. * * @param {quat} out the receiving quaternion * @param {quat} a quat to calculate conjugate of * @returns {quat} out */ quat.conjugate = function (out, a) { out[0] = -a[0]; out[1] = -a[1]; out[2] = -a[2]; out[3] = a[3]; return out; }; /** * Normalize a quat * * @param {quat} out the receiving quaternion * @param {quat} a quaternion to normalize * @returns {quat} out * @function */ quat.normalize = function (out, a) { var x = a[0], y = a[1], z = a[2], w = a[3]; var len = x * x + y * y + z * z + w * w; if (len > 0) { len = 1 / Math.sqrt(len); out[0] = x * len; out[1] = y * len; out[2] = z * len; out[3] = w * len; } return out; }; /** * Returns whether or not the quaternions have approximately the same elements in the same position (when compared with ===) * * @param {quat} a The first quaternion. * @param {quat} b The second quaternion. * @returns {Boolean} True if the vectors are equal, false otherwise. */ quat.equals = function (a, b) { var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; return Math.abs(a0 - b0) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)); }; /** * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===) * * @param {quat} a The first quaternion. * @param {quat} b The second quaternion. * @returns {Boolean} True if the vectors are equal, false otherwise. */ quat.exactEquals = function (a, b) { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; }; /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix/mat4.js * 4x4 Matrix util * modified by egjs */ /** * @class 4x4 Matrix * @name mat4 */ var mat4 = {}; /** * Creates a new identity mat4 * * @returns {mat4} a new 4x4 matrix */ mat4.create = function () { var out = new glMatrix.ARRAY_TYPE(16); out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; }; /** * Set a mat4 to the identity matrix * * @param {mat4} out the receiving matrix * @returns {mat4} out */ mat4.identity = function (out) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; }; /** * Rotates a matrix by the given angle around the X axis not using SIMD * * @param {mat4} out the receiving matrix * @param {mat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ mat4.rotateX = function (out, a, rad) { var s = Math.sin(rad), c = Math.cos(rad), a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; if (a !== out) { // If the source and destination differ, copy the unchanged rows out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } // Perform axis-specific matrix multiplication out[4] = a10 * c + a20 * s; out[5] = a11 * c + a21 * s; out[6] = a12 * c + a22 * s; out[7] = a13 * c + a23 * s; out[8] = a20 * c - a10 * s; out[9] = a21 * c - a11 * s; out[10] = a22 * c - a12 * s; out[11] = a23 * c - a13 * s; return out; }; /** * Rotates a matrix by the given angle around the Y axis not using SIMD * * @param {mat4} out the receiving matrix * @param {mat4} a the matrix to rotate * @param {Number} rad the angle to rotate the matrix by * @returns {mat4} out */ mat4.rotateY = function (out, a, rad) { var s = Math.sin(rad), c = Math.cos(rad), a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; if (a !== out) { // If the source and destination differ, copy the unchanged rows out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } // Perform axis-specific matrix multiplication out[0] = a00 * c - a20 * s; out[1] = a01 * c - a21 * s; out[2] = a02 * c - a22 * s; out[3] = a03 * c - a23 * s; out[8] = a00 * s + a20 * c; out[9] = a01 * s + a21 * c; out[10] = a02 * s + a22 * c; out[11] = a03 * s + a23 * c; return out; }; /** * Calculates a 4x4 matrix from the given quaternion * * @param {mat4} out mat4 receiving operation result * @param {quat} q Quaternion to create matrix from * * @returns {mat4} out */ mat4.fromQuat = function (out, q) { var x = q[0], y = q[1], z = q[2], w = q[3], x2 = x + x, y2 = y + y, z2 = z + z, xx = x * x2, yx = y * x2, yy = y * y2, zx = z * x2, zy = z * y2, zz = z * z2, wx = w * x2, wy = w * y2, wz = w * z2; out[0] = 1 - yy - zz; out[1] = yx + wz; out[2] = zx - wy; out[3] = 0; out[4] = yx - wz; out[5] = 1 - xx - zz; out[6] = zy + wx; out[7] = 0; out[8] = zx + wy; out[9] = zy - wx; out[10] = 1 - xx - yy; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; }; /** * Generates a perspective projection matrix with the given bounds * * @param {mat4} out mat4 frustum matrix will be written into * @param {number} fovy Vertical field of view in radians * @param {number} aspect Aspect ratio. typically viewport width/height * @param {number} near Near bound of the frustum * @param {number} far Far bound of the frustum * @returns {mat4} out */ mat4.perspective = function (out, fovy, aspect, near, far) { var f = 1.0 / Math.tan(fovy / 2), nf = 1 / (near - far); out[0] = f / aspect; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = f; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = (far + near) * nf; out[11] = -1; out[12] = 0; out[13] = 0; out[14] = 2 * far * near * nf; out[15] = 0; return out; }; /** * Original Code * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix.js * Math Util * modified by egjs */ function quatToVec3(quaternion) { var baseV = vec3.fromValues(0, 0, 1); vec3.transformQuat(baseV, baseV, quaternion); return baseV; } var util = {}; util.isPowerOfTwo = function (n) { return n && (n & n - 1) === 0; }; util.extractPitchFromQuat = function (quaternion) { var baseV = quatToVec3(quaternion); return -1 * Math.atan2(baseV[1], Math.sqrt(Math.pow(baseV[0], 2) + Math.pow(baseV[2], 2))); }; // implement reference // the general equation of a plane : http://www.gisdeveloper.co.kr/entry/평면의-공식 // calculating angle between two vectors : http://darkpgmr.tistory.com/121 var ROTATE_CONSTANT = { PITCH_DELTA: 1, YAW_DELTA_BY_ROLL: 2, YAW_DELTA_BY_YAW: 3 }; ROTATE_CONSTANT[ROTATE_CONSTANT.PITCH_DELTA] = { targetAxis: [0, 1, 0], meshPoint: [0, 0, 1] }; ROTATE_CONSTANT[ROTATE_CONSTANT.YAW_DELTA_BY_ROLL] = { targetAxis: [0, 1, 0], meshPoint: [1, 0, 0] }; ROTATE_CONSTANT[ROTATE_CONSTANT.YAW_DELTA_BY_YAW] = { targetAxis: [1, 0, 0], meshPoint: [0, 0, 1] }; function getRotationDelta(prevQ, curQ, rotateKind) { var targetAxis = vec3.fromValues(ROTATE_CONSTANT[rotateKind].targetAxis[0], ROTATE_CONSTANT[rotateKind].targetAxis[1], ROTATE_CONSTANT[rotateKind].targetAxis[2]); var meshPoint = ROTATE_CONSTANT[rotateKind].meshPoint; var prevQuaternion = quat.clone(prevQ); var curQuaternion = quat.clone(curQ); quat.normalize(prevQuaternion, prevQuaternion); quat.normalize(curQuaternion, curQuaternion); var prevPoint = vec3.fromValues(0, 0, 1); var curPoint = vec3.fromValues(0, 0, 1); vec3.transformQuat(prevPoint, prevPoint, prevQuaternion); vec3.transformQuat(curPoint, curPoint, curQuaternion); vec3.transformQuat(targetAxis, targetAxis, curQuaternion); var rotateDistance = vec3.dot(targetAxis, vec3.cross(vec3.create(), prevPoint, curPoint)); var rotateDirection = rotateDistance > 0 ? 1 : -1; // when counter clock wise, use vec3.fromValues(0,1,0) // when clock wise, use vec3.fromValues(0,-1,0) // const meshPoint1 = vec3.fromValues(0, 0, 0); var meshPoint2 = vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]); var meshPoint3; if (rotateKind !== ROTATE_CONSTANT.YAW_DELTA_BY_YAW) { meshPoint3 = vec3.fromValues(0, rotateDirection, 0); } else { meshPoint3 = vec3.fromValues(rotateDirection, 0, 0); } vec3.transformQuat(meshPoint2, meshPoint2, curQuaternion); vec3.transformQuat(meshPoint3, meshPoint3, curQuaternion); var vecU = meshPoint2; var vecV = meshPoint3; var vecN = vec3.create(); vec3.cross(vecN, vecU, vecV); vec3.normalize(vecN, vecN); var coefficientA = vecN[0]; var coefficientB = vecN[1]; var coefficientC = vecN[2]; // const coefficientD = -1 * vec3.dot(vecN, meshPoint1); // a point on the plane curPoint = vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]); vec3.transformQuat(curPoint, curPoint, curQuaternion); // a point should project on the plane prevPoint = vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]); vec3.transformQuat(prevPoint, prevPoint, prevQuaternion); // distance between prevPoint and the plane var distance = Math.abs(prevPoint[0] * coefficientA + prevPoint[1] * coefficientB + prevPoint[2] * coefficientC); var projectedPrevPoint = vec3.create(); vec3.subtract(projectedPrevPoint, prevPoint, vec3.scale(vec3.create(), vecN, distance)); var trigonometricRatio = (projectedPrevPoint[0] * curPoint[0] + projectedPrevPoint[1] * curPoint[1] + projectedPrevPoint[2] * curPoint[2]) / (vec3.length(projectedPrevPoint) * vec3.length(curPoint)); // defensive block trigonometricRatio > 1 && (trigonometricRatio = 1); var theta = Math.acos(trigonometricRatio); var crossVec = vec3.cross(vec3.create(), curPoint, projectedPrevPoint); distance = coefficientA * crossVec[0] + coefficientB * crossVec[1] + coefficientC * crossVec[2]; var thetaDirection; if (rotateKind !== ROTATE_CONSTANT.YAW_DELTA_BY_YAW) { thetaDirection = distance > 0 ? 1 : -1; } else { thetaDirection = distance < 0 ? 1 : -1; } var deltaRadian = theta * thetaDirection * rotateDirection; return glMatrix.toDegree(deltaRadian); } util.getRotationDelta = getRotationDelta; /** * Returns a number value indiciating the version of Chrome being used, * or otherwise `null` if not on Chrome. * * Ref: https://github.com/immersive-web/cardboard-vr-display/pull/19 */ /** * In Chrome m65, `devicemotion` events are broken but subsequently fixed * in 65.0.3325.148. Since many browsers use Chromium, ensure that * we scope this detection by branch and build numbers to provide * a proper fallback. * https://github.com/immersive-web/webvr-polyfill/issues/307 */ var version = -1; // It should not be null because it will be compared with number var branch = null; var build = null; var match = /Chrome\/([0-9]+)\.(?:[0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(userAgent$1); if (match) { version = parseInt(match[1], 10); branch = match[2]; build = match[3]; } var CHROME_VERSION = version; var IS_CHROME_WITHOUT_DEVICE_MOTION = version === 65 && branch === "3325" && parseInt(build, 10) < 148; var IS_ANDROID = /Android/i.test(userAgent$1); var CONTROL_MODE_VR = 1; var CONTROL_MODE_YAWPITCH = 2; var TOUCH_DIRECTION_NONE = 1; var TOUCH_DIRECTION_YAW = 2; var TOUCH_DIRECTION_PITCH = 4; var TOUCH_DIRECTION_ALL = TOUCH_DIRECTION_YAW | TOUCH_DIRECTION_PITCH; /* Const for MovableCoord */ var MC_DECELERATION = 0.0014; var MC_MAXIMUM_DURATION = 1000; var MC_BIND_SCALE = [0.20, 0.20]; var MAX_FIELD_OF_VIEW = 110; var PAN_SCALE = 320; // const DELTA_THRESHOLD = 0.015; var YAW_RANGE_HALF = 180; var PITCH_RANGE_HALF = 90; var CIRCULAR_PITCH_RANGE_HALF = 180; var GYRO_MODE = { NONE: "none", YAWPITCH: "yawPitch", VR: "VR" }; var STILLNESS_THRESHOLD = 200; // millisecond var DeviceMotion = /*#__PURE__*/ function (_Component) { _inheritsLoose(DeviceMotion, _Component); function DeviceMotion() { var _this; _this = _Component.call(this) || this; _this._onDeviceMotion = _this._onDeviceMotion.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this._onDeviceOrientation = _this._onDeviceOrientation.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this._onChromeWithoutDeviceMotion = _this._onChromeWithoutDeviceMotion.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.isWithoutDeviceMotion = IS_CHROME_WITHOUT_DEVICE_MOTION; _this.isAndroid = IS_ANDROID; _this.stillGyroVec = vec3.create(); _this.rawGyroVec = vec3.create(); _this.adjustedGyroVec = vec3.create(); _this._timer = null; _this.lastDevicemotionTimestamp = 0; _this._isEnabled = false; _this.enable(); return _this; } var _proto = DeviceMotion.prototype; _proto._onChromeWithoutDeviceMotion = function _onChromeWithoutDeviceMotion(e) { var alpha = e.alpha, beta = e.beta, gamma = e.gamma; // There is deviceorientation event trigged with empty values // on Headless Chrome. if (alpha === null) { return; } // convert to radian alpha = (alpha || 0) * Math.PI / 180; beta = (beta || 0) * Math.PI / 180; gamma = (gamma || 0) * Math.PI / 180; this.trigger("devicemotion", { inputEvent: { deviceorientation: { alpha: alpha, beta: beta, gamma: -gamma } } }); }; _proto._onDeviceOrientation = function _onDeviceOrientation() { var _this2 = this; this._timer && clearTimeout(this._timer); this._timer = setTimeout(function () { if (new Date().getTime() - _this2.lastDevicemotionTimestamp < STILLNESS_THRESHOLD) { vec3.copy(_this2.stillGyroVec, _this2.rawGyroVec); } }, STILLNESS_THRESHOLD); }; _proto._onDeviceMotion = function _onDeviceMotion(e) { // desktop chrome triggers devicemotion event with empthy sensor values. // Those events should ignored. var isGyroSensorAvailable = !(e.rotationRate.alpha == null); var isGravitySensorAvailable = !(e.accelerationIncludingGravity.x == null); if (e.interval === 0 || !(isGyroSensorAvailable && isGravitySensorAvailable)) { return; } var devicemotionEvent = _extends({}, e); devicemotionEvent.interval = e.interval; devicemotionEvent.timeStamp = e.timeStamp; devicemotionEvent.type = e.type; devicemotionEvent.rotationRate = { alpha: e.rotationRate.alpha, beta: e.rotationRate.beta, gamma: e.rotationRate.gamma }; devicemotionEvent.accelerationIncludingGravity = { x: e.accelerationIncludingGravity.x, y: e.accelerationIncludingGravity.y, z: e.accelerationIncludingGravity.z }; devicemotionEvent.acceleration = { x: e.acceleration.x, y: e.acceleration.y, z: e.acceleration.z }; if (this.isAndroid) { vec3.set(this.rawGyroVec, e.rotationRate.alpha || 0, e.rotationRate.beta || 0, e.rotationRate.gamma || 0); vec3.subtract(this.adjustedGyroVec, this.rawGyroVec, this.stillGyroVec); this.lastDevicemotionTimestamp = new Date().getTime(); devicemotionEvent.adjustedRotationRate = { alpha: this.adjustedGyroVec[0], beta: this.adjustedGyroVec[1], gamma: this.adjustedGyroVec[2] }; } this.trigger("devicemotion", { inputEvent: devicemotionEvent }); }; _proto.enable = function enable() { if (this.isAndroid) { win.addEventListener("deviceorientation", this._onDeviceOrientation); } if (this.isWithoutDeviceMotion) { win.addEventListener("deviceorientation", this._onChromeWithoutDeviceMotion); } else { win.addEventListener("devicemotion", this._onDeviceMotion); } this._isEnabled = true; }; _proto.disable = function disable() { win.removeEventListener("deviceorientation", this._onDeviceOrientation); win.removeEventListener("deviceorientation", this._onChromeWithoutDeviceMotion); win.removeEventListener("devicemotion", this._onDeviceMotion); this._isEnabled = false; }; return DeviceMotion; }(Component); ComplementaryFilter.prototype.run_ = function () { if (!this.isOrientationInitialized) { this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample); this.previousFilterQ.copy(this.accelQ); this.isOrientationInitialized = true; return; } var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; // Convert gyro rotation vector to a quaternion delta. var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT); this.gyroIntegralQ.multiply(gyroDeltaQ); // filter_1 = K * (filter_0 + gyro * dT) + (1 - K) * accel. this.filterQ.copy(this.previousFilterQ); this.filterQ.multiply(gyroDeltaQ); // Calculate the delta between the current estimated gravity and the real // gravity vector from accelerometer. var invFilterQ = new MathUtil.Quaternion(); invFilterQ.copy(this.filterQ); invFilterQ.inverse(); this.estimatedGravity.set(0, 0, -1); this.estimatedGravity.applyQuaternion(invFilterQ); this.estimatedGravity.normalize(); this.measuredGravity.copy(this.currentAccelMeasurement.sample); this.measuredGravity.normalize(); // Compare estimated gravity with measured gravity, get the delta quaternion // between the two. var deltaQ = new MathUtil.Quaternion(); deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity); deltaQ.inverse(); // Calculate the SLERP target: current orientation plus the measured-estimated // quaternion delta. var targetQ = new MathUtil.Quaternion(); targetQ.copy(this.filterQ); targetQ.multiply(deltaQ); // SLERP factor: 0 is pure gyro, 1 is pure accel. this.filterQ.slerp(targetQ, 1 - this.kFilter); this.previousFilterQ.copy(this.filterQ); if (!this.isFilterQuaternionInitialized) { this.isFilterQuaternionInitialized = true; } }; ComplementaryFilter.prototype.getOrientation = function () { if (this.isFilterQuaternionInitialized) { return this.filterQ; } else { return null; } }; var K_FILTER = 0.98; var PREDICTION_TIME_S = 0.040; var FusionPoseSensor = /*#__PURE__*/ function (_Component) { _inheritsLoose(FusionPoseSensor, _Component); function FusionPoseSensor() { var _this; _this = _Component.call(this) || this; _this.deviceMotion = new DeviceMotion(); _this.accelerometer = new MathUtil.Vector3(); _this.gyroscope = new MathUtil.Vector3(); _this._onDeviceMotionChange = _this._onDeviceMotionChange.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this._onScreenOrientationChange = _this._onScreenOrientationChange.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.filter = new ComplementaryFilter(K_FILTER); _this.posePredictor = new PosePredictor(PREDICTION_TIME_S); _this.filterToWorldQ = new MathUtil.Quaternion(); _this.isFirefoxAndroid = Util.isFirefoxAndroid(); _this.isIOS = Util.isIOS(); // Ref https://github.com/immersive-web/cardboard-vr-display/issues/18 _this.isChromeUsingDegrees = CHROME_VERSION >= 66; _this._isEnabled = false; // Set the filter to world transform, depending on OS. if (_this.isIOS) { _this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), Math.PI / 2); } else { _this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), -Math.PI / 2); } _this.inverseWorldToScreenQ = new MathUtil.Quaternion(); _this.worldToScreenQ = new MathUtil.Quaternion(); _this.originalPoseAdjustQ = new MathUtil.Quaternion(); _this.originalPoseAdjustQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), -win.orientation * Math.PI / 180); _this._setScreenTransform(); // Adjust this filter for being in landscape mode. if (Util.isLandscapeMode()) { _this.filterToWorldQ.multiply(_this.inverseWorldToScreenQ); } // Keep track of a reset transform for resetSensor. _this.resetQ = new MathUtil.Quaternion(); _this.deviceMotion.on("devicemotion", _this._onDeviceMotionChange); _this.enable(); return _this; } var _proto = FusionPoseSensor.prototype; _proto.enable = function enable() { if (this.isEnabled()) { return; } this.deviceMotion.enable(); this._isEnabled = true; win.addEventListener("orientationchange", this._onScreenOrientationChange); }; _proto.disable = function disable() { if (!this.isEnabled()) { return; } this.deviceMotion.disable(); this._isEnabled = false; win.removeEventListener("orientationchange", this._onScreenOrientationChange); }; _proto.isEnabled = function isEnabled() { return this._isEnabled; }; _proto.destroy = function destroy() { this.disable(); this.deviceMotion = null; }; _proto._triggerChange = function _triggerChange() { var orientation = this.getOrientation(); // if orientation is not prepared. don't trigger change event if (!orientation) { return; } if (!this._prevOrientation) { this._prevOrientation = orientation; return; } if (quat.equals(this._prevOrientation, orientation)) { return; } this.trigger("change", { quaternion: orientation }); }; _proto.getOrientation = function getOrientation() { var orientation; // Hack around using deviceorientation instead of devicemotion if (this.deviceMotion.isWithoutDeviceMotion && this._deviceOrientationQ) { this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () { var y = new MathUtil.Quaternion().setFromAxisAngle(new MathUtil.Vector3(0, 1, 0), -this._alpha); return y; }.bind(this)(); orientation = this._deviceOrientationQ; var out = new MathUtil.Quaternion(); out.copy(orientation); out.multiply(this.filterToWorldQ); out.multiply(this.resetQ); out.multiply(this.worldToScreenQ); out.multiplyQuaternions(this.deviceOrientationFixQ, out); // return quaternion as glmatrix quaternion object var out_ = quat.fromValues(out.x, out.y, out.z, out.w); return quat.normalize(out_, out_); } else { // Convert from filter space to the the same system used by the // deviceorientation event. orientation = this.filter.getOrientation(); if (!orientation) { return null; } var _out = this._convertFusionToPredicted(orientation); // return quaternion as glmatrix quaternion object var _out_ = quat.fromValues(_out.x, _out.y, _out.z, _out.w); return quat.normalize(_out_, _out_); } }; _proto._convertFusionToPredicted = function _convertFusionToPredicted(orientation) { // Predict orientation. this.predictedQ = this.posePredictor.getPrediction(orientation, this.gyroscope, this.previousTimestampS); // Convert to THREE coordinate system: -Z forward, Y up, X right. var out = new MathUtil.Quaternion(); out.copy(this.filterToWorldQ); out.multiply(this.resetQ); out.multiply(this.predictedQ); out.multiply(this.worldToScreenQ); return out; }; _proto._onDeviceMotionChange = function _onDeviceMotionChange(_ref) { var inputEvent = _ref.inputEvent; var deviceorientation = inputEvent.deviceorientation; var deviceMotion = inputEvent; var accGravity = deviceMotion.accelerationIncludingGravity; var rotRate = deviceMotion.adjustedRotationRate || deviceMotion.rotationRate; var timestampS = deviceMotion.timeStamp / 1000; if (deviceorientation) { if (!this._alpha) { this._alpha = deviceorientation.alpha; } this._deviceOrientationQ = this._deviceOrientationQ || new MathUtil.Quaternion(); this._deviceOrientationQ.setFromEulerYXZ(deviceorientation.beta, deviceorientation.alpha, deviceorientation.gamma); this._triggerChange(); } else { // Firefox Android timeStamp returns one thousandth of a millisecond. if (this.isFirefoxAndroid) { timestampS /= 1000; } this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); // Browsers on iOS, Firefox/Android, and Chrome m66/Android `rotationRate` // is reported in degrees, so we first convert to radians. if (this.isIOS || this.isFirefoxAndroid || this.isChromeUsingDegrees) { this.gyroscope.multiplyScalar(Math.PI / 180); } this.filter.addAccelMeasurement(this.accelerometer, timestampS); this.filter.addGyroMeasurement(this.gyroscope, timestampS); this._triggerChange(); this.previousTimestampS = timestampS; } }; _proto._onScreenOrientationChange = function _onScreenOrientationChange(screenOrientation) { this._setScreenTransform(win.orientation); }; _proto._setScreenTransform = function _setScreenTransform() { this.worldToScreenQ.set(0, 0, 0, 1); switch (win.orientation) { case 0: break; case 90: this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), 90 / -180 * Math.PI); break; case -90: this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), -90 / -180 * Math.PI); break; case 180: this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), 180 / -180 * Math.PI); break; default: break; } this.inverseWorldToScreenQ.copy(this.worldToScreenQ); this.inverseWorldToScreenQ.inverse(); }; return FusionPoseSensor; }(Component); function getDeltaYaw(prvQ, curQ) { var yawDeltaByYaw = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.YAW_DELTA_BY_YAW); var yawDeltaByRoll = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.YAW_DELTA_BY_ROLL) * Math.sin(util.extractPitchFromQuat(curQ)); return yawDeltaByRoll + yawDeltaByYaw; } function getDeltaPitch(prvQ, curQ) { var pitchDelta = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.PITCH_DELTA); return pitchDelta; } var TiltMotionInput = /*#__PURE__*/ function (_Component) { _inheritsLoose(TiltMotionInput, _Component); function TiltMotionInput(el, options) { var _this; _this = _Component.call(this) || this; _this.element = el; _this._prevQuaternion = null; _this._quaternion = null; _this.fusionPoseSensor = null; _this.options = _extends({ scale: 1, threshold: 0 }, options); _this._onPoseChange = _this._onPoseChange.bind(_assertThisInitialized(_assertThisInitialized(_this))); return _this; } var _proto = TiltMotionInput.prototype; _proto.mapAxes = function mapAxes(axes) { this.axes = axes; }; _proto.connect = function connect(observer) { if (this.observer) { return this; } this.observer = observer; this.fusionPoseSensor = new FusionPoseSensor(); this.fusionPoseSensor.enable(); this._attachEvent(); return this; }; _proto.disconnect = function disconnect() { if (!this.observer) { return this; } this._dettachEvent(); this.fusionPoseSensor.disable(); this.fusionPoseSensor.destroy(); this.fusionPoseSensor = null; this.observer = null; return this; }; _proto.destroy = function destroy() { this.disconnect(); this.element = null; this.options = null; this.axes = null; this._prevQuaternion = null; this._quaternion = null; }; _proto._onPoseChange = function _onPoseChange(event) { if (!this._prevQuaternion) { this._prevQuaternion = quat.clone(event.quaternion); this._quaternion = quat.clone(event.quaternion); return; } quat.copy(this._prevQuaternion, this._quaternion); quat.copy(this._quaternion, event.quaternion); this.observer.change(this, event, toAxis(this.axes, [getDeltaYaw(this._prevQuaternion, this._quaternion), getDeltaPitch(this._prevQuaternion, this._quaternion)])); }; _proto._attachEvent = function _attachEvent() { this.fusionPoseSensor.on("change", this._onPoseChange); }; _proto._dettachEvent = function _dettachEvent() { this.fusionPoseSensor.off("change", this._onPoseChange); }; return TiltMotionInput; }(Component); var screenRotationAngleInst = null; var refCount = 0; var ScreenRotationAngle = /*#__PURE__*/ function () { function ScreenRotationAngle() { refCount++; if (screenRotationAngleInst) { return screenRotationAngleInst; } /* eslint-disable */ screenRotationAngleInst = this; /* eslint-enable */ this._onDeviceOrientation = this._onDeviceOrientation.bind(this); this._onOrientationChange = this._onOrientationChange.bind(this); this._spinR = 0; this._screenOrientationAngle = 0; win.addEventListener("deviceorientation", this._onDeviceOrientation); win.addEventListener("orientationchange", this._onOrientationChange); } var _proto = ScreenRotationAngle.prototype; _proto._onDeviceOrientation = function _onDeviceOrientation(e) { if (e.beta === null || e.gamma === null) { // (Chrome) deviceorientation is fired with invalid information {alpha=null, beta=null, ...} despite of not dispatching it. We skip it. return; } // Radian var betaR = glMatrix.toRadian(e.beta); var gammaR = glMatrix.toRadian(e.gamma); /* spinR range = [-180, 180], left side: 0 ~ -180(deg), right side: 0 ~ 180(deg) */ this._spinR = Math.atan2(Math.cos(betaR) * Math.sin(gammaR), Math.sin(betaR)); }; _proto._onOrientationChange = function _onOrientationChange(e) { if (win.screen && win.screen.orientation && win.screen.orientation.angle !== undefined) { this._screenOrientationAngle = screen.orientation.angle; } else if (win.orientation !== undefined) { /* iOS */ this._screenOrientationAngle = win.orientation >= 0 ? win.orientation : 360 + win.orientation; } }; _proto.getRadian = function getRadian() { // Join with screen orientation // this._testVal = this._spinR + ", " + this._screenOrientationAngle + ", " + window.orientation; return this._spinR + glMatrix.toRadian(this._screenOrientationAngle); }; _proto.unref = function unref() { if (--refCount > 0) { return; } win.removeEventListener("deviceorientation", this._onDeviceOrientation); win.removeEventListener("orientationchange", this._onOrientationChange); this._spinR = 0; this._screenOrientationAngle = 0; /* eslint-disable */ screenRotationAngleInst = null; /* eslint-enable */ refCount = 0; }; return ScreenRotationAngle; }(); /** * RotationPanInput is extension of PanInput to compensate coordinates by screen rotation angle. * * The reason for using this function is that in VR mode, * the roll angle is adjusted in the direction opposite to the screen rotation angle. * * Therefore, the angle that the user touches and moves does not match the angle at which the actual object should move. * @extends PanInput */ var RotationPanInput = /*#__PURE__*/ function (_PanInput) { _inheritsLoose(RotationPanInput, _PanInput); /** * Constructor * * @private * @param {HTMLElement} el target element * @param {Object} [options] The option object * @param {Boolean} [options.useRotation] Whether to use rotation(or VR) */ function RotationPanInput(el, options) { var _this; _this = _PanInput.call(this, el, options) || this; _this._useRotation = false; _this._screenRotationAngle = null; _this.setUseRotation(!!(options && options.useRotation)); _this._userDirection = Axes.DIRECTION_ALL; return _this; } var _proto = RotationPanInput.prototype; _proto.setUseRotation = function setUseRotation(useRotation) { this._useRotation = useRotation; if (this._screenRotationAngle) { this._screenRotationAngle.unref(); this._screenRotationAngle = null; } if (this._useRotation) { this._screenRotationAngle = new ScreenRotationAngle(); } }; _proto.connect = function connect(observer) { // User intetened direction this._userDirection = this._direction; // In VR Mode, Use ALL direction if direction is not none // Because horizontal and vertical is changed dynamically by screen rotation. // this._direction is used to initialize hammerjs if (this._useRotation && this._direction & Axes.DIRECTION_ALL) { this._direction = Axes.DIRECTION_ALL; } _PanInput.prototype.connect.call(this, observer); }; _proto.getOffset = function getOffset(properties, useDirection) { if (this._useRotation === false) { return _PanInput.prototype.getOffset.call(this, properties, useDirection); } var offset = _PanInput.prototype.getOffset.call(this, properties, [true, true]); var newOffset = [0, 0]; var theta = this._screenRotationAngle.getRadian(); var cosTheta = Math.cos(theta); var sinTheta = Math.sin(theta); newOffset[0] = offset[0] * cosTheta - offset[1] * sinTheta; newOffset[1] = offset[1] * cosTheta + offset[0] * sinTheta; // Use only user allowed direction. if (!(this._userDirection & Axes.DIRECTION_HORIZONTAL)) { newOffset[0] = 0; } else if (!(this._userDirection & Axes.DIRECTION_VERTICAL)) { newOffset[1] = 0; } return newOffset; }; _proto.destroy = function destroy() { if (this._useRotation) { this._screenRotationAngle && this._screenRotationAngle.unref(); } _PanInput.prototype.destroy.call(this); }; return RotationPanInput; }(PanInput); var ORIGIN_VECTOR = vec3.fromValues(0, 0, 0); var X_AXIS_VECTOR = vec3.fromValues(1, 0, 0); var Y_AXIS_VECTOR = vec3.fromValues(0, 1, 0); var DeviceQuaternion = /*#__PURE__*/ function (_Component) { _inheritsLoose(DeviceQuaternion, _Component); function DeviceQuaternion() { var _this; _this = _Component.call(this) || this; _this._screenRotationAngle = new ScreenRotationAngle(); _this._fusionPoseSensor = new FusionPoseSensor(); _this._quaternion = quat.create(); _this._fusionPoseSensor.enable(); _this._fusionPoseSensor.on("change", function (e) { _this._quaternion = e.quaternion; _this.trigger("change", { isTrusted: true }); }); return _this; } var _proto = DeviceQuaternion.prototype; _proto.getCombinedQuaternion = function getCombinedQuaternion(yaw, pitch) { var deviceR = this._screenRotationAngle.getRadian(); // rotate x-axis around z-axis about screen rotation angle. var pitchAxis = vec3.rotateZ(vec3.create(), X_AXIS_VECTOR, ORIGIN_VECTOR, deviceR); var yawQ = quat.setAxisAngle(quat.create(), Y_AXIS_VECTOR, glMatrix.toRadian(-yaw)); // rotate quaternion around new x-axis about pitch angle. var pitchQ = quat.setAxisAngle(quat.create(), pitchAxis, glMatrix.toRadian(-pitch)); var conj = quat.conjugate(quat.create(), this._quaternion); // Multiply pitch quaternion -> device quaternion -> yaw quaternion var outQ = quat.multiply(quat.create(), pitchQ, conj); quat.multiply(outQ, outQ, yawQ); return outQ; }; _proto.destroy = function destroy() { // detach all event handler this.off(); if (this._fusionPoseSensor) { this._fusionPoseSensor.off(); this._fusionPoseSensor.destroy(); this._fusionPoseSensor = null; } if (this._screenRotationAngle) { this._screenRotationAngle.unref(); this._screenRotationAngle = null; } }; return DeviceQuaternion; }(Component); var VERSION = "3.2.2-rc"; var DEFAULT_YAW_RANGE = [-YAW_RANGE_HALF, YAW_RANGE_HALF]; var DEFAULT_PITCH_RANGE = [-PITCH_RANGE_HALF, PITCH_RANGE_HALF]; var CIRCULAR_PITCH_RANGE = [-CIRCULAR_PITCH_RANGE_HALF, CIRCULAR_PITCH_RANGE_HALF]; /** * A module used to provide coordinate based on yaw/pitch orientation. This module receives user touch action, keyboard, mouse and device orientation(if it exists) as input, then combines them and converts it to yaw/pitch coordinates. * * @alias eg.YawPitchControl * @extends eg.Component * * @support {"ie": "10+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"} */ var YawPitchControl = /*#__PURE__*/ function () { var YawPitchControl = /*#__PURE__*/ function (_Component) { _inheritsLoose(YawPitchControl, _Component); // Expose DeviceOrientationControls sub module for test purpose /** * @param {Object} options The option object of the eg.YawPitch module * @param {Element}[options.element=null] element A base element for the eg.YawPitch module * @param {Number} [options.yaw=0] initial yaw (degree) * @param {Number} [options.pitch=0] initial pitch (degree) * @param {Number} [options.fov=65] initial field of view (degree) * @param {Boolean} [optiosn.showPolePoint=true] Indicates whether pole is shown * @param {Boolean} [options.useZoom=true] Indicates whether zoom is available * @param {Boolean} [options.useKeyboard=true] Indicates whether keyboard is enabled * @param {String} [config.gyroMode=yawPitch] Enables control through device motion. * @param {Number} [options.touchDirection=TOUCH_DIRECTION_ALL] Direction of the touch movement (TOUCH_DIRECTION_ALL: all, TOUCH_DIRECTION_YAW: horizontal, TOUCH_DIRECTION_PITCH: vertical, TOUCH_DIRECTION_NONE: no mo