UNPKG

view360-canex

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,602 lines (1,366 loc) 219 kB
/* Copyright (c) 2017 NAVER Corp. name: view360-canex license: MIT author: Canex repository: https://github.com/Can-ExploreInc/egjs-view360 version: 3.4.9 */ import Component from '@egjs/component'; import Promise from 'promise-polyfill'; import agent$1 from '@egjs/agent'; import Axes, { PanInput, WheelInput, PinchInput, MoveKeyInput } from '@egjs/axes'; import { vec2, vec3, quat, glMatrix, mat4, mat3 } from 'gl-matrix'; import ImReady from '@egjs/imready'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; } || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** * 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 agent = agent$1(); var osName = agent.os.name; var browserName = agent.browser.name; var IS_IOS = osName === "ios"; var IS_SAFARI_ON_DESKTOP = osName === "mac" && browserName === "safari"; /** * 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 = 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"); var WEBXR_SUPPORTED = false; var checkXRSupport = function () { var navigator = window.navigator; if (!navigator.xr) { return; } if (navigator.xr.isSessionSupported) { navigator.xr.isSessionSupported("immersive-vr").then(function (res) { WEBXR_SUPPORTED = res; }).catch(function () {}); // tslint:disable-line no-empty } else if (navigator.xr.supportsSession) { navigator.xr.supportsSession("immersive-vr").then(function (res) { WEBXR_SUPPORTED = res; }).catch(function () {}); // tslint:disable-line no-empty } }; /** * 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; } function toDegree(a) { return a * 180 / Math.PI; } 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))); }; util.hypot = Math.hypot || function (x, y) { return Math.sqrt(x * x + y * y); }; // 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 if (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 toDegree(deltaRadian); } function angleBetweenVec2(v1, v2) { var det = v1[0] * v2[1] - v2[0] * v1[1]; var theta = -Math.atan2(det, vec2.dot(v1, v2)); return theta; } util.yawOffsetBetween = function (viewDir, targetDir) { var viewDirXZ = vec2.fromValues(viewDir[0], viewDir[2]); var targetDirXZ = vec2.fromValues(targetDir[0], targetDir[2]); vec2.normalize(viewDirXZ, viewDirXZ); vec2.normalize(targetDirXZ, targetDirXZ); var theta = -angleBetweenVec2(viewDirXZ, targetDirXZ); return theta; }; util.toDegree = toDegree; util.getRotationDelta = getRotationDelta; util.angleBetweenVec2 = angleBetweenVec2; function toAxis(source, offset) { return offset.reduce(function (acc, v, i) { if (source[i]) { acc[source[i]] = v; } return acc; }, {}); } /* * Copyright 2016 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var MathUtil = window.MathUtil || {}; MathUtil.degToRad = Math.PI / 180; MathUtil.radToDeg = 180 / Math.PI; // Some minimal math functionality borrowed from THREE.Math and stripped down // for the purposes of this library. MathUtil.Vector2 = function (x, y) { this.x = x || 0; this.y = y || 0; }; MathUtil.Vector2.prototype = { constructor: MathUtil.Vector2, set: function (x, y) { this.x = x; this.y = y; return this; }, copy: function (v) { this.x = v.x; this.y = v.y; return this; }, subVectors: function (a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } }; MathUtil.Vector3 = function (x, y, z) { this.x = x || 0; this.y = y || 0; this.z = z || 0; }; MathUtil.Vector3.prototype = { constructor: MathUtil.Vector3, set: function (x, y, z) { this.x = x; this.y = y; this.z = z; return this; }, copy: function (v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, length: function () { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); }, normalize: function () { var scalar = this.length(); if (scalar !== 0) { var invScalar = 1 / scalar; this.multiplyScalar(invScalar); } else { this.x = 0; this.y = 0; this.z = 0; } return this; }, multiplyScalar: function (scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; }, applyQuaternion: function (q) { var x = this.x; var y = this.y; var z = this.z; var qx = q.x; var qy = q.y; var qz = q.z; var qw = q.w; // calculate quat * vector var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; }, dot: function (v) { return this.x * v.x + this.y * v.y + this.z * v.z; }, crossVectors: function (a, b) { var ax = a.x, ay = a.y, az = a.z; var bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } }; MathUtil.Quaternion = function (x, y, z, w) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = w !== undefined ? w : 1; }; MathUtil.Quaternion.prototype = { constructor: MathUtil.Quaternion, set: function (x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, copy: function (quaternion) { this.x = quaternion.x; this.y = quaternion.y; this.z = quaternion.z; this.w = quaternion.w; return this; }, setFromEulerXYZ: function (x, y, z) { var c1 = Math.cos(x / 2); var c2 = Math.cos(y / 2); var c3 = Math.cos(z / 2); var s1 = Math.sin(x / 2); var s2 = Math.sin(y / 2); var s3 = Math.sin(z / 2); this.x = s1 * c2 * c3 + c1 * s2 * s3; this.y = c1 * s2 * c3 - s1 * c2 * s3; this.z = c1 * c2 * s3 + s1 * s2 * c3; this.w = c1 * c2 * c3 - s1 * s2 * s3; return this; }, setFromEulerYXZ: function (x, y, z) { var c1 = Math.cos(x / 2); var c2 = Math.cos(y / 2); var c3 = Math.cos(z / 2); var s1 = Math.sin(x / 2); var s2 = Math.sin(y / 2); var s3 = Math.sin(z / 2); this.x = s1 * c2 * c3 + c1 * s2 * s3; this.y = c1 * s2 * c3 - s1 * c2 * s3; this.z = c1 * c2 * s3 - s1 * s2 * c3; this.w = c1 * c2 * c3 + s1 * s2 * s3; return this; }, setFromAxisAngle: function (axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin(halfAngle); this.x = axis.x * s; this.y = axis.y * s; this.z = axis.z * s; this.w = Math.cos(halfAngle); return this; }, multiply: function (q) { return this.multiplyQuaternions(this, q); }, multiplyQuaternions: function (a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w; var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w; this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; return this; }, inverse: function () { this.x *= -1; this.y *= -1; this.z *= -1; this.normalize(); return this; }, normalize: function () { var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); if (l === 0) { this.x = 0; this.y = 0; this.z = 0; this.w = 1; } else { l = 1 / l; this.x = this.x * l; this.y = this.y * l; this.z = this.z * l; this.w = this.w * l; } return this; }, slerp: function (qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); var x = this.x, y = this.y, z = this.z, w = this.w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; if (cosHalfTheta < 0) { this.w = -qb.w; this.x = -qb.x; this.y = -qb.y; this.z = -qb.z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this.w = w; this.x = x; this.y = y; this.z = z; return this; } var halfTheta = Math.acos(cosHalfTheta); var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); if (Math.abs(sinHalfTheta) < 0.001) { this.w = 0.5 * (w + this.w); this.x = 0.5 * (x + this.x); this.y = 0.5 * (y + this.y); this.z = 0.5 * (z + this.z); return this; } var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this.w = w * ratioA + this.w * ratioB; this.x = x * ratioA + this.x * ratioB; this.y = y * ratioA + this.y * ratioB; this.z = z * ratioA + this.z * ratioB; return this; }, setFromUnitVectors: function () { // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final // assumes direction vectors vFrom and vTo are normalized var v1, r; var EPS = 0.000001; return function (vFrom, vTo) { if (v1 === undefined) v1 = new MathUtil.Vector3(); r = vFrom.dot(vTo) + 1; if (r < EPS) { r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { v1.set(-vFrom.y, vFrom.x, 0); } else { v1.set(0, -vFrom.z, vFrom.y); } } else { v1.crossVectors(vFrom, vTo); } this.x = v1.x; this.y = v1.y; this.z = v1.z; this.w = r; this.normalize(); return this; }; }() }; var mathUtil = MathUtil; /* * Copyright 2015 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var Util = window.Util || {}; Util.MIN_TIMESTEP = 0.001; Util.MAX_TIMESTEP = 1; Util.base64 = function (mimeType, base64) { return 'data:' + mimeType + ';base64,' + base64; }; Util.clamp = function (value, min, max) { return Math.min(Math.max(min, value), max); }; Util.lerp = function (a, b, t) { return a + (b - a) * t; }; Util.isIOS = function () { var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); return function () { return isIOS; }; }(); Util.isWebViewAndroid = function () { var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1; return function () { return isWebViewAndroid; }; }(); Util.isSafari = function () { var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); return function () { return isSafari; }; }(); Util.isFirefoxAndroid = function () { var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; return function () { return isFirefoxAndroid; }; }(); Util.isR7 = function () { var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1; return function () { return isR7; }; }(); Util.isLandscapeMode = function () { var rtn = window.orientation == 90 || window.orientation == -90; return Util.isR7() ? !rtn : rtn; }; // Helper method to validate the time steps of sensor timestamps. Util.isTimestampDeltaValid = function (timestampDeltaS) { if (isNaN(timestampDeltaS)) { return false; } if (timestampDeltaS <= Util.MIN_TIMESTEP) { return false; } if (timestampDeltaS > Util.MAX_TIMESTEP) { return false; } return true; }; Util.getScreenWidth = function () { return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio; }; Util.getScreenHeight = function () { return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio; }; Util.requestFullscreen = function (element) { if (Util.isWebViewAndroid()) { return false; } if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } else { return false; } return true; }; Util.exitFullscreen = function () { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } else { return false; } return true; }; Util.getFullscreenElement = function () { return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; }; Util.linkProgram = function (gl, vertexSource, fragmentSource, attribLocationMap) { // No error checking for brevity. var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); for (var attribName in attribLocationMap) gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); gl.linkProgram(program); gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); return program; }; Util.getProgramUniforms = function (gl, program) { var uniforms = {}; var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); var uniformName = ''; for (var i = 0; i < uniformCount; i++) { var uniformInfo = gl.getActiveUniform(program, i); uniformName = uniformInfo.name.replace('[0]', ''); uniforms[uniformName] = gl.getUniformLocation(program, uniformName); } return uniforms; }; Util.orthoMatrix = function (out, left, right, bottom, top, near, far) { var lr = 1 / (left - right), bt = 1 / (bottom - top), nf = 1 / (near - far); out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 2 * nf; out[11] = 0; out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = (far + near) * nf; out[15] = 1; return out; }; Util.copyArray = function (source, dest) { for (var i = 0, n = source.length; i < n; i++) { dest[i] = source[i]; } }; Util.isMobile = function () { var check = false; (function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera); return check; }; Util.extend = function (dest, src) { for (var key in src) { if (src.hasOwnProperty(key)) { dest[key] = src[key]; } } return dest; }; Util.safariCssSizeWorkaround = function (canvas) { // TODO(smus): Remove this workaround when Safari for iOS is fixed. // iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556). // // "To the last I grapple with thee; // from hell's heart I stab at thee; // for hate's sake I spit my last breath at thee." // -- Moby Dick, by Herman Melville if (Util.isIOS()) { var width = canvas.style.width; var height = canvas.style.height; canvas.style.width = parseInt(width) + 1 + 'px'; canvas.style.height = parseInt(height) + 'px'; setTimeout(function () { canvas.style.width = width; canvas.style.height = height; }, 100); } // Debug only. window.Util = Util; window.canvas = canvas; }; Util.isDebug = function () { return Util.getQueryParameter('debug'); }; Util.getQueryParameter = function (name) { var name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); }; Util.frameDataFromPose = function () { var piOver180 = Math.PI / 180.0; var rad45 = Math.PI * 0.25; // Borrowed from glMatrix. function mat4_perspectiveFromFieldOfView(out, fov, near, far) { var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45), downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45), leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45), rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45), xScale = 2.0 / (leftTan + rightTan), yScale = 2.0 / (upTan + downTan); out[0] = xScale; out[1] = 0.0; out[2] = 0.0; out[3] = 0.0; out[4] = 0.0; out[5] = yScale; out[6] = 0.0; out[7] = 0.0; out[8] = -((leftTan - rightTan) * xScale * 0.5); out[9] = (upTan - downTan) * yScale * 0.5; out[10] = far / (near - far); out[11] = -1.0; out[12] = 0.0; out[13] = 0.0; out[14] = far * near / (near - far); out[15] = 0.0; return out; } function mat4_fromRotationTranslation(out, q, v) { // Quaternion math 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, xy = x * y2, xz = x * z2, yy = y * y2, yz = y * z2, zz = z * z2, wx = w * x2, wy = w * y2, wz = w * z2; out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy; out[3] = 0; out[4] = xy - wz; out[5] = 1 - (xx + zz); out[6] = yz + wx; out[7] = 0; out[8] = xz + wy; out[9] = yz - wx; out[10] = 1 - (xx + yy); out[11] = 0; out[12] = v[0]; out[13] = v[1]; out[14] = v[2]; out[15] = 1; return out; } function mat4_translate(out, a, v) { var x = v[0], y = v[1], z = v[2], a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23; if (a === out) { out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; } else { a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; out[12] = a00 * x + a10 * y + a20 * z + a[12]; out[13] = a01 * x + a11 * y + a21 * z + a[13]; out[14] = a02 * x + a12 * y + a22 * z + a[14]; out[15] = a03 * x + a13 * y + a23 * z + a[15]; } return out; } function mat4_invert(out, a) { var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32, // Calculate the determinant det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return out; } var defaultOrientation = new Float32Array([0, 0, 0, 1]); var defaultPosition = new Float32Array([0, 0, 0]); function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) { mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar); var orientation = pose.orientation || defaultOrientation; var position = pose.position || defaultPosition; mat4_fromRotationTranslation(view, orientation, position); if (parameters) mat4_translate(view, view, parameters.offset); mat4_invert(view, view); } return function (frameData, pose, vrDisplay) { if (!frameData || !pose) return false; frameData.pose = pose; frameData.timestamp = pose.timestamp; updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay.getEyeParameters("left"), vrDisplay); updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay.getEyeParameters("right"), vrDisplay); return true; }; }(); Util.isInsideCrossDomainIFrame = function () { var isFramed = window.self !== window.top; var refDomain = Util.getDomainFromUrl(document.referrer); var thisDomain = Util.getDomainFromUrl(window.location.href); return isFramed && refDomain !== thisDomain; }; // From http://stackoverflow.com/a/23945027. Util.getDomainFromUrl = function (url) { var domain; // Find & remove protocol (http, ftp, etc.) and get domain. if (url.indexOf("://") > -1) { domain = url.split('/')[2]; } else { domain = url.split('/')[0]; } //find & remove port number domain = domain.split(':')[0]; return domain; }; var util$1 = Util; /* * Copyright 2015 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Given an orientation and the gyroscope data, predicts the future orientation * of the head. This makes rendering appear faster. * * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf * * @param {Number} predictionTimeS time from head movement to the appearance of * the corresponding image. */ function PosePredictor(predictionTimeS) { this.predictionTimeS = predictionTimeS; // The quaternion corresponding to the previous state. this.previousQ = new mathUtil.Quaternion(); // Previous time a prediction occurred. this.previousTimestampS = null; // The delta quaternion that adjusts the current pose. this.deltaQ = new mathUtil.Quaternion(); // The output quaternion. this.outQ = new mathUtil.Quaternion(); } var __proto = PosePredictor.prototype; __proto.getPrediction = function (currentQ, gyro, timestampS) { if (!this.previousTimestampS) { this.previousQ.copy(currentQ); this.previousTimestampS = timestampS; return currentQ; } // Calculate axis and angle based on gyroscope rotation rate data. var axis = new mathUtil.Vector3(); axis.copy(gyro); axis.normalize(); var angularSpeed = gyro.length(); // If we're rotating slowly, don't do prediction. if (angularSpeed < mathUtil.degToRad * 20) { if (util$1.isDebug()) { console.log('Moving slowly, at %s deg/s: no prediction', (mathUtil.radToDeg * angularSpeed).toFixed(1)); } this.outQ.copy(currentQ); this.previousQ.copy(currentQ); return this.outQ; } // Get the predicted angle based on the time delta and latency. var deltaT = timestampS - this.previousTimestampS; var predictAngle = angularSpeed * this.predictionTimeS; this.deltaQ.setFromAxisAngle(axis, predictAngle); this.outQ.copy(this.previousQ); this.outQ.multiply(this.deltaQ); this.previousQ.copy(currentQ); this.previousTimestampS = timestampS; return this.outQ; }; var posePredictor = PosePredictor; /** * 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); 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); 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 (_super) { __extends(DeviceMotion, _super); function DeviceMotion() { var _this = _super.call(this) || this; _this._onDeviceMotion = _this._onDeviceMotion.bind(_this); _this._onDeviceOrientation = _this._onDeviceOrientation.bind(_this); _this._onChromeWithoutDeviceMotion = _this._onChromeWithoutDeviceMotion.bind(_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 = -1; _this.lastDevicemotionTimestamp = 0; _this._isEnabled = false; _this.enable(); return _this; } var __proto = DeviceMotion.prototype; __proto.enable = function () { 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 () { win.removeEventListener("deviceorientation", this._onDeviceOrientation); win.removeEventListener("deviceorientation", this._onChromeWithoutDeviceMotion); win.removeEventListener("devicemotion", this._onDeviceMotion); this._isEnabled = false; }; __proto._onChromeWithoutDeviceMotion = function (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 () { var _this = this; if (this._timer) { clearTimeout(this._timer); } this._timer = win.setTimeout(function () { if (new Date().getTime() - _this.lastDevicemotionTimestamp < STILLNESS_THRESHOLD) { vec3.copy(_this.stillGyroVec, _this.rawGyroVec); } }, STILLNESS_THRESHOLD); }; __proto._onDeviceMotion = function (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 = __assign({}, 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 }); }; return DeviceMotion; }(Component); function SensorSample(sample, timestampS) { this.set(sample, timestampS); } var __proto$1 = SensorSample.prototype; __proto$1.set = function (sample, timestampS) { this.sample = sample; this.timestampS = timestampS; }; __proto$1.copy = function (sensorSample) { this.set(sensorSample.sample, sensorSample.timestampS); }; var sensorSample = SensorSample; /* * Copyright 2015 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * An implementation of a simple complementary filter, which fuses gyroscope and * accelerometer data from the 'devicemotion' event. * * Accelerometer data is very noisy, but stable over the long term. * Gyroscope data is smooth, but tends to drift over the long term. * * This fusion is relatively simple: * 1. Get orientation estimates from accelerometer by applying a low-pass filter * on that data. * 2. Get orientation estimates from gyroscope by integrating over time. * 3. Combine the two estimates, weighing (1) in the long term, but (2) for the * short term. */ function ComplementaryFilter(kFilter) { this.kFilter = kFilter; // Raw sensor measurements. this.currentAccelMeasurement = new sensorSample(); this.currentGyroMeasurement = new sensorSample(); this.previousGyroMeasurement = new sensorSample(); // Set default look direction to be in the correct direction. if (util$1.isIOS()) { this.filterQ = new mathUtil.Quaternion(-1, 0, 0, 1); } else { this.filterQ = new mathUtil.Quaternion(1, 0, 0, 1); } this.previousFilterQ = new mathUtil.Quaternion(); this.previousFilterQ.copy(this.filterQ); // Orientation based on the accelerometer. this.accelQ = new mathUtil.Quaternion(); // Whether or not the orientation has been initialized. this.isOrientationInitialized = false; // Running estimate of gravity based on the current orientation. this.estimatedGravity = new mathUtil.Vector3(); // Measured gravity based on accelerometer. this.measuredGravity = new mathUtil.Vector3(); // Debug only quaternion of gyro-based orientation. this.gyroIntegralQ = new mathUtil.Quaternion(); } var __proto$2 = ComplementaryFilter.prototype; __proto$2.addAccelMeasurement = function (vector, timestampS) { this.currentAccelMeasurement.set(vector, timestampS); }; __proto$2.addGyroMeasurement = function (vector, timestampS) { this.currentGyroMeasurement.set(vector, timestampS); var deltaT = timestampS - this.previousGyroMeasurement.timestampS; if (util$1.isTimestampDeltaValid(deltaT)) { this.run_(); } this.previousGyroMeasurement.copy(this.currentGyroMeasurement); }; __proto$2.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(); if (util$1.isDebug()) { console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', mathUtil.radToDeg * util$1.getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1)); } // 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); }; __proto$2.getOrientation = function () { return this.filterQ; }; __proto$2.accelToQuaternion_ = function (accel) { var normAccel = new mathUtil.Vector3(); normAccel.copy(accel); normAccel.normalize(); var quat = new mathUtil.Quaternion(); quat.setFromUnitVectors(new mathUtil.Vector3(0, 0, -1), normAccel); quat.inverse(); return quat; }; __proto$2.gyroToQuaternionDelta_ = function (gyro, dt) { // Extract axis and angle from the gyroscope data. var quat = new mathUtil.Quaternion(); var axis = new mathUtil.Vector3(); axis.copy(gyro); axis.normalize(); quat.setFromAxisAngle(axis, gyro.length() * dt); return quat; }; var complementaryFilter = ComplementaryFilter; 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 (_super) { __extends(FusionPoseSensor