panolens
Version:
Javascript panorama viewer
1,877 lines (1,282 loc) • 252 kB
JavaScript
import { Cache, Texture, RGBFormat, RGBAFormat, CubeTexture, EventDispatcher, VideoTexture, LinearFilter, SpriteMaterial, Sprite, Color, CanvasTexture, DoubleSide, Vector3, Mesh, BackSide, Object3D, SphereBufferGeometry, MeshBasicMaterial, BufferGeometry, BufferAttribute, ShaderLib, BoxBufferGeometry, ShaderMaterial, Matrix4, Vector2, Quaternion, PlaneBufferGeometry, Math as Math$1, MOUSE, PerspectiveCamera, OrthographicCamera, Euler, Scene, StereoCamera, WebGLRenderTarget, NearestFilter, WebGLRenderer, Raycaster, Frustum, REVISION as REVISION$1 } from 'three';
const version="0.12.1";const dependencies={three:"^0.105.2"};
/**
* REVISION
* @module REVISION
* @example PANOLENS.REVISION
* @type {string} revision
*/
const REVISION = version.split( '.' )[ 1 ];
/**
* VERSION
* @module VERSION
* @example PANOLENS.VERSION
* @type {string} version
*/
const VERSION = version;
/**
* THREEJS REVISION
* @module THREE_REVISION
* @example PANOLENS.THREE_REVISION
* @type {string} threejs revision
*/
const THREE_REVISION = dependencies.three.split( '.' )[ 1 ];
/**
* THREEJS VERSION
* @module THREE_VERSION
* @example PANOLENS.THREE_VERSION
* @type {string} threejs version
*/
const THREE_VERSION = dependencies.three.replace( /[^0-9.]/g, '' );
/**
* CONTROLS
* @module CONTROLS
* @example PANOLENS.CONTROLS.ORBIT
* @property {number} ORBIT 0
* @property {number} DEVICEORIENTATION 1
*/
const CONTROLS = { ORBIT: 0, DEVICEORIENTATION: 1 };
/**
* MODES
* @module MODES
* @example PANOLENS.MODES.UNKNOWN
* @property {number} UNKNOWN 0
* @property {number} NORMAL 1
* @property {number} CARDBOARD 2
* @property {number} STEREO 3
*/
const MODES = { UNKNOWN: 0, NORMAL: 1, CARDBOARD: 2, STEREO: 3 };
/**
* Data URI Images
* @module DataImage
* @example PANOLENS.DataImage.Info
* @property {string} Info Information Icon
* @property {string} Arrow Arrow Icon
* @property {string} FullscreenEnter Fullscreen Enter Icon
* @property {string} FullscreenLeave Fullscreen Leave Icon
* @property {string} VideoPlay Video Play Icon
* @property {string} VideoPause Video Pause Icon
* @property {string} WhiteTile White Tile Icon
* @property {string} Setting Settings Icon
* @property {string} ChevronRight Chevron Right Icon
* @property {string} Check Check Icon
* @property {string} ViewIndicator View Indicator Icon
*/
const DataImage = {
Info: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAABAAAAAQADq8/hgAAADBklEQVR42u2bP08UQRiHnzFaSYCI/xoksdBIqGwIiYWRUBISExpCQ0ej38FWOmlIKKhoMPEbaCxsrrHiYrQgOSlQEaICrT+LHSPZzNzt3s3c3Hn7lHvLzvv82L2dm30XKioqKgYY062BJF0HpoA7wARwBbhsPz4DjoEG8AnYNcZ8Sx1Op8IXJM1KWpdUV3nq9m9nJV1I7VNGfEzSM0mNNqR9NOwxx1L7NRMflbQm6SSgeJ4TO8Zoat+8/LKkg4jieQ4kLaf2RtKwpJ0uiufZkTScSn5S0l5C+b/sSZrstvyMpKPU5uc4kjTTjkvpeYCkaeA1/+7hvcIZMGuMqUULQNIU8Aa4ltrWwyHwyBizGzwASSPAe+B2assW7AH3jTE/i+xcZoa12Qfy2Bo3i+5cKABl99zF1GYlWFTBeULLS0DZrOsDcDNggTXgc27bLWA64BhfgHvGmB8dHUXZ1DM0S45xliKMs9bKr+klIOkqsBrwv9JtVq1DewEAT4Ch1BYdMGQdygeg7Df4SmqDAKyoyXpCszPgITCeuvoAjFuX0gE8jljUdv7bCtiOOJ7XpdUZ8L/gdXHOA5QtYH5NXXVgbrgWWn1nwFTqaiPgdPIFcDd1tRFwOl307DwRuZgXwLvctgfA04hjOp18AcReZ6sZY16e3yDpUuQxnU6+S2AkcjEpcDr1zxOXSPgCKLSa0mc4nXwB/EpdbQScTr4AGqmrjYDTyRfAx9TVRsDp5Aug8LJyH+F0cgZg58z11BUHpO5ruGh2G3ybuuqAeF2aBfAqddUB8bq0OgP2U1cegH3aOQOMMb+BrdTVB2DLupQLwLIOnKY26IBT6+ClaQDGmO/ARmqLDtiwDn7HVkcY+EdjNoTlCI+tYhO2iUppm6HKslPUq2qQKHpUe8AFsjaUXuUQWCgqXyoAG8IuME/WkNRrnAHzZfqDSgdgQ6gBc2Td3b3CMTBXtkOsIzTIjZLnQhjcVtlcEIPZLJ0LoVvt8s/Va+3yuSAG84UJRxB98cpM9dJURUVFxSDzBxKde4Lk3/h2AAAAAElFTkSuQmCC',
Arrow: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAABAAAAAQADq8/hgAAADPklEQVR42u2bMUscQRiG30/SRaJEI1ZKUiRErNIELRUbQYSAnX8hpVUgkDYp0wgWVjYW+QcJaQzYpLojJIXhtDDEKBpj65ti58ixmdmb2ZvZ7+T2AUHudmfmeXf2bnb3O6CmpqZmgJGqOiI5AWAWwEMA0wDuArht3r4CcAagBeAbgIaI/NQOp1fhIZKLJN+SbDKcptl3keSQtk+I+BjJVyRbJaRdtEybY9p+ReKjJN+QvIwonufS9DGq7ZuXXyd5nFA8zzHJdW1vkLxDcrdC8Ty7JO9oyc+QPFCUb3NAcqZq+TmSp9rmHZySnCvjErwOIPkUwHv8+w7vF64ALIrIfrIASM4C+ADgnratgxMACyLSiB4AyREAnwE80LbswgGAJyJy4bNxyApr6wbIw4xxy3djrwCYfeeuaZsFsEbPdULXU4DZqusLgMkEA21P05EEbf8A8FhEzos28pkBLxLKL5s/r/M1kEkz9vKQHGeatf05yfmOfubNa7G5JDle5NhtBjwHMBz5yFwAWBaRT+0XzP8pZsKwcQiH2fX8Ycojb+kzxUw4ZJn7CSQXqpRPHMKCq7+iZJ71Mvdy/DftXSQ6HcJdSDaqPPKW/mPOBO+lcbvzCU35RCFM2PpwnQKzZQfdgfe0dxH5dLA6uQJ4pC2fIASrkyuA6X6QjxyC1ckVQNn7bNHlI4ZgdXIFUObiJJl8pBCsTjGfuIwA2Cv4FN7xbYjkjqsRAHuIePXoCiDF1Zk2VidXAL+1R5sAq5MrgJb2aBNgdXIF8FV7tAmwOrkCCFs73wysTtYATHFCU3vEEWm6Ci6KvgY/ao86Ik6XogDeaY86Ik6XbjPgSHvkEThCwQy45XpDRK5JbgN4GWkgUyR9H65MRQxgW0SunZ5FezK7pfwd8e8MV8UfAPdF5Jdrg8JrAbPjprZFD2wWyQP6j8ZSEufRmGlgQ9umBBvd5IOgbjFUKLu+XnWBhG+rpsFVZGUo/coJgFVf+aAATAgNACvICpL6jSsAKyH1QcEBmBD2ASwhq+7uF84ALIVWiPUEB7lQsiOEwS2VzQUxmMXSuRCqKpd/zX4rl88FMZg/mLAEcSN+MlP/aKqmpqZmkPkL0hSjwOpNKxwAAAAASUVORK5CYII=',
FullscreenEnter: 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4KICAgIDxwYXRoIGQ9Ik03IDE0SDV2NWg1di0ySDd2LTN6bS0yLTRoMlY3aDNWNUg1djV6bTEyIDdoLTN2Mmg1di01aC0ydjN6TTE0IDV2MmgzdjNoMlY1aC01eiIvPgo8L3N2Zz4=',
FullscreenLeave: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggc3R5bGU9ImZpbGw6I2ZmZiIgZD0iTTE0LDE0SDE5VjE2SDE2VjE5SDE0VjE0TTUsMTRIMTBWMTlIOFYxNkg1VjE0TTgsNUgxMFYxMEg1VjhIOFY1TTE5LDhWMTBIMTRWNUgxNlY4SDE5WiIgLz48L3N2Zz4=',
VideoPlay: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggc3R5bGU9ImZpbGw6I2ZmZiIgZD0iTTgsNS4xNFYxOS4xNEwxOSwxMi4xNEw4LDUuMTRaIiAvPjwvc3ZnPg==',
VideoPause: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggc3R5bGU9ImZpbGw6I2ZmZiIgZD0iTTE0LDE5LjE0SDE4VjUuMTRIMTRNNiwxOS4xNEgxMFY1LjE0SDZWMTkuMTRaIiAvPjwvc3ZnPg==',
WhiteTile: 'data:image/png;base64,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',
Setting: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAABAAAAAQADq8/hgAAADn0lEQVR42u2bzUsVURjGnyO6CPzAMnTjppAo3LTwH1CqTfaxbeOiRS37A0wXtROFVi1aRBs3LWohSIGbQAQXViBGRhG0UIRKUCpK7q/FnOB2uc6cOXNmRnGe3eW+H8/7zLln3vNxpQoVKlQ4wjBFJAFOSRqX1O7osivpvjHmU1nChBZglvSYLYJbS0EanCvIJzWK+gnsyH34/8OuMaYjb265jwCgz6N4SWq3vodbAEmnS/KtBDgoAgyU5BteAOAkMAPcBroc7PskDWfgN+wyDwBdltMMcDI3tYBnde/pHeARMNTErgd4APzweP834oeN1dMkz5DlsFNn/yyv4kdiSK4At4AO4CqwGaDwRmza2B0210qM7YhrXU59ANAq6bWkwQTTn5KO5fIE0uVYlXTeGLOXFMx1DrjlULwKKN41x6DlnIjEEQCckPRe0okCiguJr5LOGGO+xhm5jICJQ1i8LOeJJKPYEQAMKvrtt5ZdjSf2FM0Fq/sZJI2A6UNcvCz36TiDfUcAcE1SPu/U6Mm8k/TFfu6XdFb5iX3dGPM8lQfwNod3+TowBnQ3yddtv1vPIe+b1JIBiwEJ1IAJ208k5W21trWA+V/5CHAcmAtU/A2P/DcCiTAHHE8tgCVhgLvAXgYCk17Jo/yTGfLuWe7Zd72AC8CWB4n3OAz7mLytNkZabAEXMhfeQKYfWEpJZCxA3rGUOZeA/qDF15FpAz47EvlNk9neI2e3jeWCz0BbmvipNkSMMX8kuSZYM8Z8zyqAjbHmaN5mOeYjgIXrU93MWrxHrNQjrqiDkQMLHwG+OdqF3NN3jeXKzU8AoF1SzdH8XKhJUO7HZDXLMbwAwICkJUULFxe0SbqSVQAbw3Xi7Ze0ZLmGAzAKbHs0JGU1QtvAaIjCW4B7ZOvJy2qFa5a730RPtBiaz0CgnkiZi6F5fBZDVMvho7EhcuS3xJJ2hV9IupgTqaLw0hhzab8vq23xOG/r+LDsKjLgYVzxUnU0ltwK2wDezUyJmEwqXgp/PL4rvxthaeCSI+zxuA10J8ZkWdJNSb2SLkvayKHwDRu71+ZajrG941J8agALDQ3GU/a/IvMkYCPzmCbtLNEVmacNtgs5iP9fYVNEV1Q6Hez7yNZSL+J2SarTcpqiyV2iUkG0IvPFvbz5FbEn+KEk3wMjwMeSfCsBXFBdly9CAPk9ydyffpECuB5tZfVJjaKWueOSfinln6YK4lahQoUKRxd/AcRPGTcQCAUQAAAAAElFTkSuQmCC',
ChevronRight: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNTksMTYuNThMMTMuMTcsMTJMOC41OSw3LjQxTDEwLDZMMTYsMTJMMTAsMThMOC41OSwxNi41OFoiIC8+PC9zdmc+',
Check: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIxLDdMOSwxOUwzLjUsMTMuNUw0LjkxLDEyLjA5TDksMTYuMTdMMTkuNTksNS41OUwyMSw3WiIgLz48L3N2Zz4=',
ViewIndicator: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBpZD0idmlldy1pbmRpY2F0b3IiIGhlaWdodD0iMzAiIHdpZHRoPSIzMCIgdmlld0JveD0iLTIuNSAtMSAzMCAzMCI+Cgk8c3R5bGUgdHlwZT0idGV4dC9jc3MiPi5zdDB7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLW1pdGVybGltaXQ6MTA7ZmlsbDpub25lO30uc3Qxe3N0cm9rZS13aWR0aDo2O3N0cm9rZS1taXRlcmxpbWl0OjEwO30KCTwvc3R5bGU+Cgk8Zz4KCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNIDEyLjUgMCBBIDEyLjUgMTIuNSAwIDAgMCAtMTIuNSAwIEEgMTIuNSAxMi41IDAgMCAwIDEyLjUgMCIgdHJhbnNmb3JtPSJtYXRyaXgoMSwwLDAsMSwxMywxNS41KSI+PC9wYXRoPgoJCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0gMTMgMCBMIDEwIDIgTCAxNiAyIFoiPjwvcGF0aD4KCQk8cGF0aCBjbGFzcz0ic3QyIiBkPSJNIDIgMCBBIDIgMiAwIDAgMCAtMiAwIEEgMiAyIDAgMCAwIDIgMCIgdHJhbnNmb3JtPSJtYXRyaXgoMSwwLDAsMSwxMywxNS41KSI+PC9wYXRoPgoJCTxwYXRoIGNsYXNzPSJzdDEiIGlkPSJpbmRpY2F0b3IiIHRyYW5zZm9ybT0ibWF0cml4KDEsMCwwLDEsMTMsMTUuNSkiPjwvcGF0aD4KCTwvZz4KPC9zdmc+'
};
/**
* @module ImageLoader
* @description Image loader with progress based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js}
*/
const ImageLoader = {
/**
* Load image
* @example PANOLENS.ImageLoader.load( IMAGE_URL )
* @method load
* @param {string} url - An image url
* @param {function} onLoad - On load callback
* @param {function} onProgress - In progress callback
* @param {function} onError - On error callback
*/
load: function ( url, onLoad = () => {}, onProgress = () => {}, onError = () => {} ) {
// Enable cache
Cache.enabled = true;
let cached, request, arrayBufferView, blob, urlCreator, image, reference;
// Reference key
for (let iconName in DataImage) {
if (DataImage.hasOwnProperty(iconName) && url === DataImage[iconName]) {
reference = iconName;
}
}
// Cached
cached = Cache.get(reference ? reference : url);
if (cached !== undefined) {
if (onLoad) {
setTimeout(function () {
onProgress({loaded: 1, total: 1});
onLoad(cached);
}, 0);
}
return cached;
}
// Construct a new XMLHttpRequest
urlCreator = window.URL || window.webkitURL;
image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
// Add to cache
Cache.add(reference ? reference : url, image);
const onImageLoaded = () => {
urlCreator.revokeObjectURL(image.src);
onLoad(image);
};
if (url.indexOf('data:') === 0) {
image.addEventListener('load', onImageLoaded, false);
image.src = url;
return image;
}
image.crossOrigin = this.crossOrigin !== undefined ? this.crossOrigin : '';
request = new window.XMLHttpRequest();
request.open('GET', url, true);
if (process.env.npm_lifecycle_event !== 'test') {
/* istanbul ignore next */
request.onreadystatechange = function () {
if (this.readyState === 4 && this.status >= 400) {
onError();
}
};
}
request.responseType = 'arraybuffer';
request.addEventListener( 'error', onError );
request.addEventListener( 'progress', event => {
if ( !event ) return;
const { loaded, total, lengthComputable } = event;
if ( lengthComputable ) {
onProgress( { loaded, total } );
}
} );
request.addEventListener( 'loadend', event => {
if ( !event ) return;
const { currentTarget: { response } } = event;
arrayBufferView = new Uint8Array( response );
blob = new window.Blob( [ arrayBufferView ] );
image.addEventListener( 'load', onImageLoaded, false );
image.src = urlCreator.createObjectURL( blob );
} );
request.send(null);
}
};
/**
* @module TextureLoader
* @description Texture loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/TextureLoader.js}
*/
const TextureLoader = {
/**
* Load image texture
* @example PANOLENS.TextureLoader.load( IMAGE_URL )
* @method load
* @param {string} url - An image url
* @param {function} onLoad - On load callback
* @param {function} onProgress - In progress callback
* @param {function} onError - On error callback
* @return {THREE.Texture} - Image texture
*/
load: function ( url, onLoad = () => {}, onProgress, onError ) {
var texture = new Texture();
ImageLoader.load( url, function ( image ) {
texture.image = image;
// JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.
const isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0;
texture.format = isJPEG ? RGBFormat : RGBAFormat;
texture.needsUpdate = true;
onLoad( texture );
}, onProgress, onError );
return texture;
}
};
/**
* @module CubeTextureLoader
* @description Cube Texture Loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/CubeTextureLoader.js}
*/
const CubeTextureLoader = {
/**
* Load 6 images as a cube texture
* @example PANOLENS.CubeTextureLoader.load( [ 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' ] )
* @method load
* @param {array} urls - array of 6 urls to images, one for each side of the CubeTexture. The urls should be specified in the following order: pos-x, neg-x, pos-y, neg-y, pos-z, neg-z
* @param {function} onLoad - On load callback
* @param {function} onProgress - In progress callback
* @param {function} onError - On error callback
* @return {THREE.CubeTexture} - Cube texture
*/
load: function ( urls, onLoad = () => {}, onProgress = () => {}, onError ) {
var texture, loaded, progress, all, loadings;
texture = new CubeTexture( [] );
loaded = 0;
progress = {};
all = {};
urls.map( function ( url, index ) {
ImageLoader.load( url, function ( image ) {
texture.images[ index ] = image;
loaded++;
if ( loaded === 6 ) {
texture.needsUpdate = true;
onLoad( texture );
}
}, function ( event ) {
progress[ index ] = { loaded: event.loaded, total: event.total };
all.loaded = 0;
all.total = 0;
loadings = 0;
for ( var i in progress ) {
loadings++;
all.loaded += progress[ i ].loaded;
all.total += progress[ i ].total;
}
if ( loadings < 6 ) {
all.total = all.total / loadings * 6;
}
onProgress( all );
}, onError );
} );
return texture;
}
};
/**
* @classdesc User Media
* @constructor
* @param {object} [constraints={ video: { width: { ideal: 1920 }, height: { ideal: 1080 }, facingMode: { exact: 'environment' } }, audio: false }]
*/
function Media ( constraints ) {
const defaultConstraints = { video: { width: { ideal: 1920 }, height: { ideal: 1080 }, facingMode: { exact: 'environment' } }, audio: false };
this.constraints = Object.assign( defaultConstraints, constraints );
this.container = null;
this.scene = null;
this.element = null;
this.devices = [];
this.stream = null;
this.ratioScalar = 1;
this.videoDeviceIndex = 0;
}
Media.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
setContainer: function ( container ) {
this.container = container;
},
setScene: function ( scene ) {
this.scene = scene;
},
/**
* Enumerate devices
* @memberOf Media
* @instance
* @returns {Promise}
*/
enumerateDevices: function () {
const devices = this.devices;
const resolvedPromise = new Promise( resolve => { resolve( devices ); } );
return devices.length > 0 ? resolvedPromise : window.navigator.mediaDevices.enumerateDevices();
},
/**
* Switch to next available video device
* @memberOf Media
* @instance
*/
switchNextVideoDevice: function () {
const stop = this.stop.bind( this );
const start = this.start.bind( this );
const setVideDeviceIndex = this.setVideDeviceIndex.bind( this );
let index = this.videoDeviceIndex;
this.getDevices( 'video' )
.then( devices => {
stop();
index++;
if ( index >= devices.length ) {
setVideDeviceIndex( 0 );
index--;
} else {
setVideDeviceIndex( index );
}
start( devices[ index ] );
} );
},
/**
* Get devices
* @param {string} type - type keyword to match device.kind
* @memberOf Media
* @instance
*/
getDevices: function ( type = 'video' ) {
const devices = this.devices;
const validate = _devices => {
return _devices.map( device => {
if ( !devices.includes( device ) ) { devices.push( device ); }
return device;
} );
};
const filter = _devices => {
const reg = new RegExp( type, 'i' );
return _devices.filter( device => reg.test( device.kind ) );
};
return this.enumerateDevices()
.then( validate )
.then( filter );
},
/**
* Get user media
* @param {MediaStreamConstraints} constraints
* @memberOf Media
* @instance
*/
getUserMedia: function ( constraints ) {
const setMediaStream = this.setMediaStream.bind( this );
const playVideo = this.playVideo.bind( this );
const onCatchError = error => { console.warn( `PANOLENS.Media: ${error}` ); };
return window.navigator.mediaDevices.getUserMedia( constraints )
.then( setMediaStream )
.then( playVideo )
.catch( onCatchError );
},
/**
* Set video device index
* @param {number} index
* @memberOf Media
* @instance
*/
setVideDeviceIndex: function ( index ) {
this.videoDeviceIndex = index;
},
/**
* Start streaming
* @param {MediaDeviceInfo} [targetDevice]
* @memberOf Media
* @instance
*/
start: function( targetDevice ) {
const constraints = this.constraints;
const getUserMedia = this.getUserMedia.bind( this );
const onVideoDevices = devices => {
if ( !devices || devices.length === 0 ) {
throw Error( 'no video device found' );
}
const device = targetDevice || devices[ 0 ];
constraints.video.deviceId = device.deviceId;
return getUserMedia( constraints );
};
this.element = this.createVideoElement();
return this.getDevices().then( onVideoDevices );
},
/**
* Stop streaming
* @memberOf Media
* @instance
*/
stop: function () {
const stream = this.stream;
if ( stream && stream.active ) {
const track = stream.getTracks()[ 0 ];
track.stop();
window.removeEventListener( 'resize', this.onWindowResize.bind( this ) );
this.element = null;
this.stream = null;
}
},
/**
* Set media stream
* @param {MediaStream} stream
* @memberOf Media
* @instance
*/
setMediaStream: function ( stream ) {
this.stream = stream;
this.element.srcObject = stream;
if ( this.scene ) {
this.scene.background = this.createVideoTexture();
}
window.addEventListener( 'resize', this.onWindowResize.bind( this ) );
},
/**
* Play video element
* @memberOf Media
* @instance
*/
playVideo: function () {
const { element } = this;
if ( element ) {
element.play();
this.dispatchEvent( { type: 'play' } );
}
},
/**
* Pause video element
* @memberOf Media
* @instance
*/
pauseVideo: function () {
const { element } = this;
if ( element ) {
element.pause();
this.dispatchEvent( { type: 'pause' } );
}
},
/**
* Create video texture
* @memberOf Media
* @instance
* @returns {THREE.VideoTexture}
*/
createVideoTexture: function () {
const video = this.element;
const texture = new VideoTexture( video );
texture.generateMipmaps = false;
texture.minFilter = LinearFilter;
texture.magFilter = LinearFilter;
texture.format = RGBFormat;
texture.center.set( 0.5, 0.5 );
video.addEventListener( 'canplay', this.onWindowResize.bind( this ) );
return texture;
},
/**
* Create video element
* @memberOf Media
* @instance
* @returns {HTMLVideoElement}
* @fires Media#canplay
*/
createVideoElement: function() {
const dispatchEvent = this.dispatchEvent.bind( this );
const video = document.createElement( 'video' );
/**
* Video can play event
* @type {object}
* @event Media#canplay
*/
const canPlay = () => dispatchEvent( { type: 'canplay' } );
video.setAttribute( 'autoplay', '' );
video.setAttribute( 'muted', '' );
video.setAttribute( 'playsinline', '' );
video.style.position = 'absolute';
video.style.top = '0';
video.style.left = '0';
video.style.width = '100%';
video.style.height = '100%';
video.style.objectPosition = 'center';
video.style.objectFit = 'cover';
video.style.display = this.scene ? 'none' : '';
video.addEventListener( 'canplay', canPlay );
return video;
},
/**
* On window resize event
* @param {Event} event
* @memberOf Media
* @instance
*/
onWindowResize: function () {
if ( this.element && this.element.videoWidth && this.element.videoHeight && this.scene ) {
const { clientWidth: width, clientHeight: height } = this.container;
const texture = this.scene.background;
const { videoWidth, videoHeight } = this.element;
const cameraRatio = videoHeight / videoWidth;
const viewportRatio = this.container ? width / height : 1.0;
const ratio = cameraRatio * viewportRatio * this.ratioScalar;
if ( width > height ) {
texture.repeat.set( ratio, 1 );
} else {
texture.repeat.set( 1, 1 / ratio );
}
}
}
} );
/**
* @classdesc Reticle 3D Sprite
* @constructor
* @param {THREE.Color} [color=0xffffff] - Color of the reticle sprite
* @param {boolean} [autoSelect=true] - Auto selection
* @param {number} [dwellTime=1500] - Duration for dwelling sequence to complete
*/
function Reticle ( color = 0xffffff, autoSelect = true, dwellTime = 1500 ) {
this.dpr = window.devicePixelRatio;
const { canvas, context } = this.createCanvas();
const material = new SpriteMaterial( { color, map: this.createCanvasTexture( canvas ) } );
Sprite.call( this, material );
this.canvasWidth = canvas.width;
this.canvasHeight = canvas.height;
this.context = context;
this.color = color instanceof Color ? color : new Color( color );
this.autoSelect = autoSelect;
this.dwellTime = dwellTime;
this.rippleDuration = 500;
this.position.z = -10;
this.center.set( 0.5, 0.5 );
this.scale.set( 0.5, 0.5, 1 );
this.startTimestamp = null;
this.timerId = null;
this.callback = null;
this.frustumCulled = false;
this.updateCanvasArcByProgress( 0 );
}
Reticle.prototype = Object.assign( Object.create( Sprite.prototype ), {
constructor: Reticle,
/**
* Set material color
* @param {THREE.Color} color
* @memberOf Reticle
* @instance
*/
setColor: function ( color ) {
this.material.color.copy( color instanceof Color ? color : new Color( color ) );
},
/**
* Create canvas texture
* @param {HTMLCanvasElement} canvas
* @memberOf Reticle
* @instance
* @returns {THREE.CanvasTexture}
*/
createCanvasTexture: function ( canvas ) {
const texture = new CanvasTexture( canvas );
texture.minFilter = LinearFilter;
texture.magFilter = LinearFilter;
texture.generateMipmaps = false;
return texture;
},
/**
* Create canvas element
* @memberOf Reticle
* @instance
* @returns {object} object
* @returns {HTMLCanvasElement} object.canvas
* @returns {CanvasRenderingContext2D} object.context
*/
createCanvas: function () {
const width = 32;
const height = 32;
const canvas = document.createElement( 'canvas' );
const context = canvas.getContext( '2d' );
const dpr = this.dpr;
canvas.width = width * dpr;
canvas.height = height * dpr;
context.scale( dpr, dpr );
context.shadowBlur = 5;
context.shadowColor = 'rgba(200,200,200,0.9)';
return { canvas, context };
},
/**
* Update canvas arc by progress
* @param {number} progress
* @memberOf Reticle
* @instance
*/
updateCanvasArcByProgress: function ( progress ) {
const context = this.context;
const { canvasWidth, canvasHeight, material } = this;
const dpr = this.dpr;
const degree = progress * Math.PI * 2;
const color = this.color.getStyle();
const x = canvasWidth * 0.5 / dpr;
const y = canvasHeight * 0.5 / dpr;
const lineWidth = 3;
context.clearRect( 0, 0, canvasWidth, canvasHeight );
context.beginPath();
if ( progress === 0 ) {
context.arc( x, y, canvasWidth / 16, 0, 2 * Math.PI );
context.fillStyle = color;
context.fill();
} else {
context.arc( x, y, canvasWidth / 4 - lineWidth, -Math.PI / 2, -Math.PI / 2 + degree );
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
}
context.closePath();
material.map.needsUpdate = true;
},
/**
* Ripple effect
* @memberOf Reticle
* @instance
* @fires Reticle#reticle-ripple-start
* @fires Reticle#reticle-ripple-end
*/
ripple: function () {
const context = this.context;
const { canvasWidth, canvasHeight, material } = this;
const duration = this.rippleDuration;
const timestamp = performance.now();
const color = this.color;
const dpr = this.dpr;
const x = canvasWidth * 0.5 / dpr;
const y = canvasHeight * 0.5 / dpr;
const update = () => {
const timerId = window.requestAnimationFrame( update );
const elapsed = performance.now() - timestamp;
const progress = elapsed / duration;
const opacity = 1.0 - progress > 0 ? 1.0 - progress : 0;
const radius = progress * canvasWidth * 0.5 / dpr;
context.clearRect( 0, 0, canvasWidth, canvasHeight );
context.beginPath();
context.arc( x, y, radius, 0, Math.PI * 2 );
context.fillStyle = `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, ${opacity})`;
context.fill();
context.closePath();
if ( progress >= 1.0 ) {
window.cancelAnimationFrame( timerId );
this.updateCanvasArcByProgress( 0 );
/**
* Reticle ripple end event
* @type {object}
* @event Reticle#reticle-ripple-end
*/
this.dispatchEvent( { type: 'reticle-ripple-end' } );
}
material.map.needsUpdate = true;
};
/**
* Reticle ripple start event
* @type {object}
* @event Reticle#reticle-ripple-start
*/
this.dispatchEvent( { type: 'reticle-ripple-start' } );
update();
},
/**
* Make reticle visible
* @memberOf Reticle
* @instance
*/
show: function () {
this.visible = true;
},
/**
* Make reticle invisible
* @memberOf Reticle
* @instance
*/
hide: function () {
this.visible = false;
},
/**
* Start dwelling
* @param {function} callback
* @memberOf Reticle
* @instance
* @fires Reticle#reticle-start
*/
start: function ( callback ) {
if ( !this.autoSelect ) {
return;
}
/**
* Reticle start event
* @type {object}
* @event Reticle#reticle-start
*/
this.dispatchEvent( { type: 'reticle-start' } );
this.startTimestamp = performance.now();
this.callback = callback;
this.update();
},
/**
* End dwelling
* @memberOf Reticle
* @instance
* @fires Reticle#reticle-end
*/
end: function(){
if ( !this.startTimestamp ) { return; }
window.cancelAnimationFrame( this.timerId );
this.updateCanvasArcByProgress( 0 );
this.callback = null;
this.timerId = null;
this.startTimestamp = null;
/**
* Reticle end event
* @type {object}
* @event Reticle#reticle-end
*/
this.dispatchEvent( { type: 'reticle-end' } );
},
/**
* Update dwelling
* @memberOf Reticle
* @instance
* @fires Reticle#reticle-update
*/
update: function () {
this.timerId = window.requestAnimationFrame( this.update.bind( this ) );
const elapsed = performance.now() - this.startTimestamp;
const progress = elapsed / this.dwellTime;
this.updateCanvasArcByProgress( progress );
/**
* Reticle update event
* @type {object}
* @event Reticle#reticle-update
*/
this.dispatchEvent( { type: 'reticle-update', progress } );
if ( progress >= 1.0 ) {
window.cancelAnimationFrame( this.timerId );
if ( this.callback ) { this.callback(); }
this.end();
this.ripple();
}
}
} );
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var Tween = createCommonjsModule(function (module, exports) {
/**
* Tween.js - Licensed under the MIT license
* https://github.com/tweenjs/tween.js
* ----------------------------------------------
*
* See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
* Thank you all, you're awesome!
*/
var _Group = function () {
this._tweens = {};
this._tweensAddedDuringUpdate = {};
};
_Group.prototype = {
getAll: function () {
return Object.keys(this._tweens).map(function (tweenId) {
return this._tweens[tweenId];
}.bind(this));
},
removeAll: function () {
this._tweens = {};
},
add: function (tween) {
this._tweens[tween.getId()] = tween;
this._tweensAddedDuringUpdate[tween.getId()] = tween;
},
remove: function (tween) {
delete this._tweens[tween.getId()];
delete this._tweensAddedDuringUpdate[tween.getId()];
},
update: function (time, preserve) {
var tweenIds = Object.keys(this._tweens);
if (tweenIds.length === 0) {
return false;
}
time = time !== undefined ? time : TWEEN.now();
// Tweens are updated in "batches". If you add a new tween during an update, then the
// new tween will be updated in the next batch.
// If you remove a tween during an update, it may or may not be updated. However,
// if the removed tween was added during the current batch, then it will not be updated.
while (tweenIds.length > 0) {
this._tweensAddedDuringUpdate = {};
for (var i = 0; i < tweenIds.length; i++) {
var tween = this._tweens[tweenIds[i]];
if (tween && tween.update(time) === false) {
tween._isPlaying = false;
if (!preserve) {
delete this._tweens[tweenIds[i]];
}
}
}
tweenIds = Object.keys(this._tweensAddedDuringUpdate);
}
return true;
}
};
var TWEEN = new _Group();
TWEEN.Group = _Group;
TWEEN._nextId = 0;
TWEEN.nextId = function () {
return TWEEN._nextId++;
};
// Include a performance.now polyfill.
// In node.js, use process.hrtime.
if (typeof (self) === 'undefined' && typeof (process) !== 'undefined' && process.hrtime) {
TWEEN.now = function () {
var time = process.hrtime();
// Convert [seconds, nanoseconds] to milliseconds.
return time[0] * 1000 + time[1] / 1000000;
};
}
// In a browser, use self.performance.now if it is available.
else if (typeof (self) !== 'undefined' &&
self.performance !== undefined &&
self.performance.now !== undefined) {
// This must be bound, because directly assigning this function
// leads to an invocation exception in Chrome.
TWEEN.now = self.performance.now.bind(self.performance);
}
// Use Date.now if it is available.
else if (Date.now !== undefined) {
TWEEN.now = Date.now;
}
// Otherwise, use 'new Date().getTime()'.
else {
TWEEN.now = function () {
return new Date().getTime();
};
}
TWEEN.Tween = function (object, group) {
this._object = object;
this._valuesStart = {};
this._valuesEnd = {};
this._valuesStartRepeat = {};
this._duration = 1000;
this._repeat = 0;
this._repeatDelayTime = undefined;
this._yoyo = false;
this._isPlaying = false;
this._reversed = false;
this._delayTime = 0;
this._startTime = null;
this._easingFunction = TWEEN.Easing.Linear.None;
this._interpolationFunction = TWEEN.Interpolation.Linear;
this._chainedTweens = [];
this._onStartCallback = null;
this._onStartCallbackFired = false;
this._onUpdateCallback = null;
this._onRepeatCallback = null;
this._onCompleteCallback = null;
this._onStopCallback = null;
this._group = group || TWEEN;
this._id = TWEEN.nextId();
};
TWEEN.Tween.prototype = {
getId: function () {
return this._id;
},
isPlaying: function () {
return this._isPlaying;
},
to: function (properties, duration) {
this._valuesEnd = Object.create(properties);
if (duration !== undefined) {
this._duration = duration;
}
return this;
},
duration: function duration(d) {
this._duration = d;
return this;
},
start: function (time) {
this._group.add(this);
this._isPlaying = true;
this._onStartCallbackFired = false;
this._startTime = time !== undefined ? typeof time === 'string' ? TWEEN.now() + parseFloat(time) : time : TWEEN.now();
this._startTime += this._delayTime;
for (var property in this._valuesEnd) {
// Check if an Array was provided as property value
if (this._valuesEnd[property] instanceof Array) {
if (this._valuesEnd[property].length === 0) {
continue;
}
// Create a local copy of the Array with the start value at the front
this._valuesEnd[property] = [this._object[property]].concat(this._valuesEnd[property]);
}
// If `to()` specifies a property that doesn't exist in the source object,
// we should not set that property in the object
if (this._object[property] === undefined) {
continue;
}
// Save the starting value.
this._valuesStart[property] = this._object[property];
if ((this._valuesStart[property] instanceof Array) === false) {
this._valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
}
this._valuesStartRepeat[property] = this._valuesStart[property] || 0;
}
return this;
},
stop: function () {
if (!this._isPlaying) {
return this;
}
this._group.remove(this);
this._isPlaying = false;
if (this._onStopCallback !== null) {
this._onStopCallback(this._object);
}
this.stopChainedTweens();
return this;
},
end: function () {
this.update(Infinity);
return this;
},
stopChainedTweens: function () {
for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) {
this._chainedTweens[i].stop();
}
},
group: function (group) {
this._group = group;
return this;
},
delay: function (amount) {
this._delayTime = amount;
return this;
},
repeat: function (times) {
this._repeat = times;
return this;
},
repeatDelay: function (amount) {
this._repeatDelayTime = amount;
return this;
},
yoyo: function (yoyo) {
this._yoyo = yoyo;
return this;
},
easing: function (easingFunction) {
this._easingFunction = easingFunction;
return this;
},
interpolation: function (interpolationFunction) {
this._interpolationFunction = interpolationFunction;
return this;
},
chain: function () {
this._chainedTweens = arguments;
return this;
},
onStart: function (callback) {
this._onStartCallback = callback;
return this;
},
onUpdate: function (callback) {
this._onUpdateCallback = callback;
return this;
},
onRepeat: function onRepeat(callback) {
this._onRepeatCallback = callback;
return this;
},
onComplete: function (callback) {
this._onCompleteCallback = callback;
return this;
},
onStop: function (callback) {
this._onStopCallback = callback;
return this;
},
update: function (time) {
var property;
var elapsed;
var value;
if (time < this._startTime) {
return true;
}
if (this._onStartCallbackFired === false) {
if (this._onStartCallback !== null) {
this._onStartCallback(this._object);
}
this._onStartCallbackFired = true;
}
elapsed = (time - this._startTime) / this._duration;
elapsed = (this._duration === 0 || elapsed > 1) ? 1 : elapsed;
value = this._easingFunction(elapsed);
for (property in this._valuesEnd) {
// Don't update properties that do not exist in the source object
if (this._valuesStart[property] === undefined) {
continue;
}
var start = this._valuesStart[property] || 0;
var end = this._valuesEnd[property];
if (end instanceof Array) {
this._object[property] = this._interpolationFunction(end, value);
} else {
// Parses relative end values with start as base (e.g.: +10, -3)
if (typeof (end) === 'string') {
if (end.charAt(0) === '+' || end.charAt(0) === '-') {
end = start + parseFloat(end);
} else {
end = parseFloat(end);
}
}
// Protect against non numeric properties.
if (typeof (end) === 'number') {
this._object[property] = start + (end - start) * value;
}
}
}
if (this._onUpdateCallback !== null) {
this._onUpdateCallback(this._object, elapsed);
}
if (elapsed === 1) {
if (this._repeat > 0) {
if (isFinite(this._repeat)) {
this._repeat--;
}
// Reassign starting values, restart by making startTime = now
for (property in this._valuesStartRepeat) {
if (typeof (this._valuesEnd[property]) === 'string') {
this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]);
}
if (this._yoyo) {
var tmp = this._valuesStartRepeat[property];
this._valuesStartRepeat[property] = this._valuesEnd[property];
this._valuesEnd[property] = tmp;
}
this._valuesStart[property] = this._valuesStartRepeat[property];
}
if (this._yoyo) {
this._reversed = !this._reversed;
}
if (this._repeatDelayTime !== undefined) {
this._startTime = time + this._repeatDelayTime;
} else {
this._startTime = time + this._delayTime;
}
if (this._onRepeatCallback !== null) {
this._onRepeatCallback(this._object);
}
return true;
} else {
if (this._onCompleteCallback !== null) {
this._onCompleteCallback(this._object);
}
for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) {
// Make the chained tweens start exactly at the time they should,
// even if the `update()` method was called way past the duration of the tween
this._chainedTweens[i].start(this._startTime + this._duration);
}
return false;
}
}
return true;
}
};
TWEEN.Easing = {
Linear: {
None: function (k) {
return k;
}
},
Quadratic: {
In: function (k) {
return k * k;
},
Out: function (k) {
return k * (2 - k);
},
InOut: function (k) {
if ((k *= 2) < 1) {
return 0.5 * k * k;
}
return - 0.5 * (--k * (k - 2) - 1);
}
},
Cubic: {
In: function (k) {
return k * k * k;
},
Out: function (k) {
return --k * k * k + 1;
},
InOut: function (k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k;
}
return 0.5 * ((k -= 2) * k * k + 2);
}
},
Quartic: {
In: function (k) {
return k * k * k * k;
},
Out: function (k) {
return 1 - (--k * k * k * k);
},
InOut: function (k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k * k;
}
return - 0.5 * ((k -= 2) * k * k * k - 2);
}
},
Quintic: {
In: function (k) {
return k * k * k * k * k;
},
Out: function (k) {
return --k * k * k * k * k + 1;
},
InOut: function (k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k * k * k;
}
return 0.5 * ((k -= 2) * k * k * k * k + 2);
}
},
Sinusoidal: {
In: function (k) {
return 1 - Math.cos(k * Math.PI / 2);
},
Out: function (k) {
return Math.sin(k * Math.PI / 2);
},
InOut: function (k) {
return 0.5 * (1 - Math.cos(Math.PI * k));
}
},
Exponential: {
In: function (k) {
return k === 0 ? 0 : Math.pow(1024, k - 1);
},
Out: function (k) {
return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k);
},
InOut: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
if ((k *= 2) < 1) {
return 0.5 * Math.pow(1024, k - 1);
}
return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2);
}
},
Circular: {
In: function (k) {
return 1 - Math.sqrt(1 - k * k);
},
Out: function (k) {
return Math.sqrt(1 - (--k * k));
},
InOut: function (k) {
if ((k *= 2) < 1) {
return - 0.5 * (Math.sqrt(1 - k * k) - 1);
}
return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
}
},
Elastic: {
In: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
},
Out: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1;
},
InOut: function (k) {
if (k === 0) {
return 0;
}
if (k === 1) {
return 1;
}
k *= 2;
if (k < 1) {
return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
}
return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1;
}
},
Back: {
In: function (k) {
var s = 1.70158;
return k * k * ((s + 1) * k - s);
},
Out: function (k) {
var s = 1.70158;
return --k * k * ((s + 1) * k + s) + 1;
},
InOut: function (k) {
var s = 1.70158 * 1.525;
if ((k *= 2) < 1) {
return 0.5 * (k * k * ((s + 1) * k - s));
}
return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
}
},
Bounce: {
In: function (k) {
return 1 - TWEEN.Easing.Bounce.Out(1 - k);
},
Out: function (k) {
if (k < (1 / 2.75)) {
return 7.5625 * k * k;
} else if (k < (2 / 2.75)) {
return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
} else if (k < (2.5 / 2.75)) {
return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
} else {
return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
}
},
InOut: function (k) {
if (k < 0.5) {
return TWEEN.Easing.Bounce.In(k * 2) * 0.5;
}
return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;
}
}
};
TWEEN.Interpolation = {
Linear: function (v, k) {
var m = v.length - 1;
var f = m * k;
var i = Math.floor(f);
var fn = TWEEN.Interpolation.Utils.Linear;
if (k < 0) {
return fn(v[0], v[1], f);
}
if (k > 1) {
return fn(v[m], v[m - 1], m - f);
}
return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
},
Bezier: function (v, k) {
var b = 0;
var n = v.length - 1;
var pw = Math.pow;
var bn = TWEEN.Interpolation.Utils.Bernstein;
for (var i = 0; i <= n; i++) {
b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
}
return b;
},
CatmullRom: function (v, k) {
var m = v.length - 1;
var f = m * k;
var i = Math.floor(f);
var fn = TWEEN.Interpolation.Utils.CatmullRom;
if (v[0] === v[m]) {
if (k < 0) {
i = Math.floor(f = m * (1 + k));
}
return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
} else {
if (k < 0) {
return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
}
if (k > 1) {
return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
}
return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
}
},
Utils: {
Linear: function (p0, p1, t) {
return (p1 - p0) * t + p0;
},
Bernstein: function (n, i) {
var fc = TWEEN.Interpolation.Utils.Factorial;
return fc(n) / fc(i) / fc(n - i);
},
Factorial: (function () {
var a = [1];
return function (n) {
var s = 1;
if (a[n]) {
return a[n];
}
for (var i = n; i > 1; i--) {
s *= i;
}
a[n] = s;
return s;
};
})(