UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

2,426 lines (1,570 loc) 1.57 MB
const HAS_WEBXR_DEVICE_API = navigator.xr != null && self.XRSession != null && self.XRDevice != null && self.XRDevice.prototype.supportsSession != null; const HAS_WEBXR_HIT_TEST_API = HAS_WEBXR_DEVICE_API && self.XRSession.prototype.requestHitTest; const HAS_FULLSCREEN_API = document.documentElement != null && document.documentElement.requestFullscreen != null; const HAS_RESIZE_OBSERVER = self.ResizeObserver != null; const HAS_INTERSECTION_OBSERVER = self.IntersectionObserver != null; const IS_WEBXR_AR_CANDIDATE = HAS_WEBXR_HIT_TEST_API && HAS_FULLSCREEN_API; const IS_MOBILE = (() => { const userAgent = navigator.userAgent || navigator.vendor || self.opera; let check = false; 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(userAgent) || /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(userAgent.substr(0, 4))) { check = true; } return check; })(); const HAS_OFFSCREEN_CANVAS = Boolean(self.OffscreenCanvas); const OFFSCREEN_CANVAS_SUPPORT_BITMAP = Boolean(self.OffscreenCanvas) && Boolean(self.OffscreenCanvas.prototype.transferToImageBitmap); const IS_ANDROID = /android/i.test(navigator.userAgent); const IS_IOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !self.MSStream) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); const IS_AR_QUICKLOOK_CANDIDATE = (() => { const tempAnchor = document.createElement('a'); return Boolean(tempAnchor.relList && tempAnchor.relList.supports && tempAnchor.relList.supports('ar')); })(); const IS_IOS_CHROME = IS_IOS && /CriOS\//.test(navigator.userAgent); const IS_IOS_SAFARI = IS_IOS && /Safari\//.test(navigator.userAgent); const IS_IE11 = !(window.ActiveXObject) && 'ActiveXObject' in window; const $time = Symbol('time'); const $duration = Symbol('duration'); class Timer { constructor(duration) { this[$duration] = duration; this[$time] = 0; } get time() { return this[$time]; } get timeScale() { return this[$time] / this[$duration]; } get duration() { return this[$duration]; } get hasStopped() { return this[$time] >= this[$duration]; } reset() { this[$time] = 0; } stop() { this[$time] = this[$duration]; } tick(deltaTime) { this[$time] += deltaTime; if (this.time >= this[$duration]) { this[$time] = this[$duration]; } } } const deserializeUrl = (url) => (url != null && url !== 'null') ? toFullUrl(url) : null; const assertIsArCandidate = () => { if (IS_WEBXR_AR_CANDIDATE) { return; } const missingApis = []; if (!HAS_FULLSCREEN_API) { missingApis.push('Fullscreen API'); } if (!HAS_WEBXR_DEVICE_API) { missingApis.push('WebXR Device API'); } if (!HAS_WEBXR_HIT_TEST_API) { missingApis.push('WebXR Hit Test API'); } throw new Error(`The following APIs are required for AR, but are missing in this browser: ${missingApis.join(', ')}`); }; const toFullUrl = (partialUrl) => { const url = new URL(partialUrl, window.location.toString()); return url.toString(); }; const throttle = (fn, ms) => { let timer = null; const throttled = (...args) => { if (timer != null) { return; } fn(...args); timer = self.setTimeout(() => timer = null, ms); }; throttled.flush = () => { if (timer != null) { self.clearTimeout(timer); timer = null; } }; return throttled; }; const debounce = (fn, ms) => { let timer = null; return (...args) => { if (timer != null) { self.clearTimeout(timer); } timer = self.setTimeout(() => { timer = null; fn(...args); }, ms); }; }; const step = (edge, value) => { return value < edge ? 0 : 1; }; const clamp = (value, lowerLimit, upperLimit) => Math.max(lowerLimit === -Infinity ? value : lowerLimit, Math.min(upperLimit === Infinity ? value : upperLimit, value)); const CAPPED_DEVICE_PIXEL_RATIO = 1; const resolveDpr = (() => { const HAS_META_VIEWPORT_TAG = (() => { const metas = document.head != null ? Array.from(document.head.querySelectorAll('meta')) : []; for (const meta of metas) { if (meta.name === 'viewport') { return true; } } return false; })(); if (!HAS_META_VIEWPORT_TAG) { console.warn('No <meta name="viewport"> detected; <model-viewer> will cap pixel density at 1.'); } return () => HAS_META_VIEWPORT_TAG ? window.devicePixelRatio : CAPPED_DEVICE_PIXEL_RATIO; })(); const getFirstMapKey = (map) => { if (map.keys != null) { return map.keys().next().value || null; } let firstKey = null; try { map.forEach((_value, key, _map) => { firstKey = key; throw new Error(); }); } catch (_error) { } return firstKey; }; const expect = chai.expect; var timerSpec = () => { suite('Timer', () => { test('has not ended by default', () => { const timer = new Timer(20); expect(timer.hasStopped).to.be.false; }); test('can tick', () => { const timer = new Timer(20); expect(timer.hasStopped).to.be.false; timer.tick(30); expect(timer.hasStopped).to.be.true; }); test('can reset', () => { const timer = new Timer(20); expect(timer.hasStopped).to.be.false; timer.tick(30); expect(timer.hasStopped).to.be.true; timer.reset(); expect(timer.hasStopped).to.be.false; }); test('can stop', () => { const timer = new Timer(20); expect(timer.hasStopped).to.be.false; timer.stop(); expect(timer.hasStopped).to.be.true; expect(timer.time).to.equal(20); expect(timer.timeScale).to.equal(1); timer.reset(); expect(timer.hasStopped).to.be.false; }); test('can get time', () => { const timer = new Timer(20); expect(timer.time).to.equal(0); expect(timer.timeScale).to.equal(0); timer.tick(10); expect(timer.time).to.equal(10); expect(timer.timeScale).to.equal(0.5); timer.tick(10); expect(timer.time).to.equal(20); expect(timer.timeScale).to.equal(1); timer.tick(10); expect(timer.time).to.equal(20); expect(timer.timeScale).to.equal(1); }); }); }; const expect$1 = chai.expect; suite('utils', () => { suite('deserializeUrl', () => { test('returns a string given a string', () => { expect$1(deserializeUrl('foo')).to.be.a('string'); }); test('returns null given a null-ish value', () => { expect$1(deserializeUrl(null)).to.be.equal(null); }); test('yields a url on the same origin for relative paths', () => { const { origin } = window.location; expect$1(deserializeUrl('foo').indexOf(origin)).to.be.equal(0); }); }); suite('resolveDpr', () => { suite('when <meta name="viewport"> is present', () => { test('resolves the device pixel ratio', () => { const resolvedDpr = resolveDpr(); const actualDpr = self.devicePixelRatio; expect$1(resolvedDpr).to.be.equal(actualDpr); }); }); suite('when <meta name="viewport"> is not present', () => { test.skip('caps the device pixel ratio to 1', () => { }); }); }); suite('step', () => { test('returns 0 for values below edge', () => { expect$1(step(0.5, 0.1)).to.be.equal(0); }); test('returns 1 for values above edge', () => { expect$1(step(0.5, 0.9)).to.be.equal(1); }); }); suite('clamp', () => { test('numbers below lower limit adjusted to lower limit', () => { expect$1(clamp(1.0, 2.0, 3.0)).to.be.equal(2.0); }); test('numbers above upper limit adjusted to upper limit', () => { expect$1(clamp(4.0, 2.0, 3.0)).to.be.equal(3.0); }); test('numbers within lower and upper limits unchanged', () => { expect$1(clamp(2.5, 2.0, 3.0)).to.be.equal(2.5); }); }); timerSpec(); }); const parseValues = (valuesString) => { return whitespaceSplit(valuesString.trim()) .map(valueString => parseAtomicValue(valueString)); }; const parseAtomicValue = (() => { const VALUE_AND_UNIT_RE = /^((?:(?:#|[a-zA-Z])[a-zA-Z\d]*)|(?:-?[\d.]+))([a-zA-Z%]*)$/; return (valueString) => { const match = valueString.match(VALUE_AND_UNIT_RE) || []; return { type: 'value', value: match[1], unit: match[2] || null }; }; })(); const whitespaceSplit = (() => { const WHITESPACE_RE = /\s+/g; return (inputString) => { if (!inputString) { return []; } return inputString.split(WHITESPACE_RE); }; })(); const expect$2 = chai.expect; const valueNode = (value, unit = null) => ({ type: 'value', value, unit }); suite('parsers', () => { suite('parseValues', () => { test('parses single CSS color strings', () => { expect$2(parseValues('red')).to.be.eql([valueNode('red')]); }); test('parses multiple CSS color strings', () => { expect$2(parseValues('red #00f #00ff00')) .to.be.eql([valueNode('red'), valueNode('#00f'), valueNode('#00ff00')]); }); test('parses single CSS length values', () => { expect$2(parseValues('123deg')).to.be.eql([valueNode('123', 'deg')]); }); test('parses multiple CSS length values', () => { expect$2(parseValues('10% 123deg 2rad 1mm')).to.be.eql([ valueNode('10', '%'), valueNode('123', 'deg'), valueNode('2', 'rad'), valueNode('1', 'mm') ]); }); test('parses mixed value types', () => { expect$2(parseValues('3.14cm red')) .to.be.eql([valueNode('3.14', 'cm'), valueNode('red')]); }); test('is resilient to awkward whitespace', () => { expect$2(parseValues(' 4')).to.be.eql([valueNode('4')]); expect$2(parseValues('red ')).to.be.eql([valueNode('red')]); expect$2(parseValues(' 1px \n2px #f00')) .to.be.eql([valueNode('1', 'px'), valueNode('2', 'px'), valueNode('#f00')]); }); }); }); // Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } if ( Number.isInteger === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger Number.isInteger = function ( value ) { return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value; }; } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( 'name' in Function.prototype === false ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, 'name', { get: function () { return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( 'Cannot convert undefined or null to object' ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; var listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } }, hasEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return false; var listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { var index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; var array = listenerArray.slice( 0 ); for ( var i = 0, l = array.length; i < l; i ++ ) { array[ i ].call( this, event ); } } } } ); var REVISION = '105'; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; var CullFaceFront = 2; var CullFaceFrontBack = 3; var FrontFaceDirectionCW = 0; var FrontFaceDirectionCCW = 1; var BasicShadowMap = 0; var PCFShadowMap = 1; var PCFSoftShadowMap = 2; var FrontSide = 0; var BackSide = 1; var DoubleSide = 2; var FlatShading = 1; var SmoothShading = 2; var NoColors = 0; var FaceColors = 1; var VertexColors = 2; var NoBlending = 0; var NormalBlending = 1; var AdditiveBlending = 2; var SubtractiveBlending = 3; var MultiplyBlending = 4; var CustomBlending = 5; var AddEquation = 100; var SubtractEquation = 101; var ReverseSubtractEquation = 102; var MinEquation = 103; var MaxEquation = 104; var ZeroFactor = 200; var OneFactor = 201; var SrcColorFactor = 202; var OneMinusSrcColorFactor = 203; var SrcAlphaFactor = 204; var OneMinusSrcAlphaFactor = 205; var DstAlphaFactor = 206; var OneMinusDstAlphaFactor = 207; var DstColorFactor = 208; var OneMinusDstColorFactor = 209; var SrcAlphaSaturateFactor = 210; var NeverDepth = 0; var AlwaysDepth = 1; var LessDepth = 2; var LessEqualDepth = 3; var EqualDepth = 4; var GreaterEqualDepth = 5; var GreaterDepth = 6; var NotEqualDepth = 7; var MultiplyOperation = 0; var MixOperation = 1; var AddOperation = 2; var NoToneMapping = 0; var LinearToneMapping = 1; var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; var ACESFilmicToneMapping = 5; var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; var EquirectangularReflectionMapping = 303; var EquirectangularRefractionMapping = 304; var SphericalReflectionMapping = 305; var CubeUVReflectionMapping = 306; var CubeUVRefractionMapping = 307; var RepeatWrapping = 1000; var ClampToEdgeWrapping = 1001; var MirroredRepeatWrapping = 1002; var NearestFilter = 1003; var NearestMipMapNearestFilter = 1004; var NearestMipMapLinearFilter = 1005; var LinearFilter = 1006; var LinearMipMapNearestFilter = 1007; var LinearMipMapLinearFilter = 1008; var UnsignedByteType = 1009; var ByteType = 1010; var ShortType = 1011; var UnsignedShortType = 1012; var IntType = 1013; var UnsignedIntType = 1014; var FloatType = 1015; var HalfFloatType = 1016; var UnsignedShort4444Type = 1017; var UnsignedShort5551Type = 1018; var UnsignedShort565Type = 1019; var UnsignedInt248Type = 1020; var AlphaFormat = 1021; var RGBFormat = 1022; var RGBAFormat = 1023; var LuminanceFormat = 1024; var LuminanceAlphaFormat = 1025; var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; var RedFormat = 1028; var RGB_S3TC_DXT1_Format = 33776; var RGBA_S3TC_DXT1_Format = 33777; var RGBA_S3TC_DXT3_Format = 33778; var RGBA_S3TC_DXT5_Format = 33779; var RGB_PVRTC_4BPPV1_Format = 35840; var RGB_PVRTC_2BPPV1_Format = 35841; var RGBA_PVRTC_4BPPV1_Format = 35842; var RGBA_PVRTC_2BPPV1_Format = 35843; var RGB_ETC1_Format = 36196; var RGBA_ASTC_4x4_Format = 37808; var RGBA_ASTC_5x4_Format = 37809; var RGBA_ASTC_5x5_Format = 37810; var RGBA_ASTC_6x5_Format = 37811; var RGBA_ASTC_6x6_Format = 37812; var RGBA_ASTC_8x5_Format = 37813; var RGBA_ASTC_8x6_Format = 37814; var RGBA_ASTC_8x8_Format = 37815; var RGBA_ASTC_10x5_Format = 37816; var RGBA_ASTC_10x6_Format = 37817; var RGBA_ASTC_10x8_Format = 37818; var RGBA_ASTC_10x10_Format = 37819; var RGBA_ASTC_12x10_Format = 37820; var RGBA_ASTC_12x12_Format = 37821; var LoopOnce = 2200; var LoopRepeat = 2201; var LoopPingPong = 2202; var InterpolateDiscrete = 2300; var InterpolateLinear = 2301; var InterpolateSmooth = 2302; var ZeroCurvatureEnding = 2400; var ZeroSlopeEnding = 2401; var WrapAroundEnding = 2402; var TrianglesDrawMode = 0; var TriangleStripDrawMode = 1; var TriangleFanDrawMode = 2; var LinearEncoding = 3000; var sRGBEncoding = 3001; var GammaEncoding = 3007; var RGBEEncoding = 3002; var LogLuvEncoding = 3003; var RGBM7Encoding = 3004; var RGBM16Encoding = 3005; var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; var TangentSpaceNormalMap = 0; var ObjectSpaceNormalMap = 1; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var _Math = { DEG2RAD: Math.PI / 180, RAD2DEG: 180 / Math.PI, generateUUID: ( function () { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 var lut = []; for ( var i = 0; i < 256; i ++ ) { lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); } return function generateUUID() { var d0 = Math.random() * 0xffffffff | 0; var d1 = Math.random() * 0xffffffff | 0; var d2 = Math.random() * 0xffffffff | 0; var d3 = Math.random() * 0xffffffff | 0; var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' + lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' + lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] + lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ]; // .toUpperCase() here flattens concatenated strings to save heap memory space. return uuid.toUpperCase(); }; } )(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, // Linear mapping from range <a1, a2> to range <b1, b2> mapLinear: function ( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); }, // https://en.wikipedia.org/wiki/Linear_interpolation lerp: function ( x, y, t ) { return ( 1 - t ) * x + t * y; }, // http://en.wikipedia.org/wiki/Smoothstep smoothstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); }, smootherstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); }, // Random integer from <low, high> interval randInt: function ( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, // Random float from <low, high> interval randFloat: function ( low, high ) { return low + Math.random() * ( high - low ); }, // Random float from <-range/2, range/2> interval randFloatSpread: function ( range ) { return range * ( 0.5 - Math.random() ); }, degToRad: function ( degrees ) { return degrees * _Math.DEG2RAD; }, radToDeg: function ( radians ) { return radians * _Math.RAD2DEG; }, isPowerOfTwo: function ( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; }, ceilPowerOfTwo: function ( value ) { return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); }, floorPowerOfTwo: function ( value ) { return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author philogb / http://blog.thejit.org/ * @author egraether / http://egraether.com/ * @author zz85 / http://www.lab4games.net/zz85/blog */ function Vector2( x, y ) { this.x = x || 0; this.y = y || 0; } Object.defineProperties( Vector2.prototype, { "width": { get: function () { return this.x; }, set: function ( value ) { this.x = value; } }, "height": { get: function () { return this.y; }, set: function ( value ) { this.y = value; } } } ); Object.assign( Vector2.prototype, { isVector2: true, set: function ( x, y ) { this.x = x; this.y = y; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( 'index is out of range: ' + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( 'index is out of range: ' + index ); } }, clone: function () { return new this.constructor( this.x, this.y ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, applyMatrix3: function ( m ) { var x = this.x, y = this.y; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; }, clampScalar: function ( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); return this; }, clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y; }, cross: function ( v ) { return this.x * v.y - this.y * v.x; }, lengthSq: function () { return this.x * this.x + this.y * this.y; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, angle: function () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; }, manhattanDistanceTo: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; }, fromBufferAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; }, rotateAround: function ( center, angle ) { var c = Math.cos( angle ), s = Math.sin( angle ); var x = this.x - center.x; var y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Object.assign( Quaternion, { slerp: function ( qa, qb, qm, t ) { return qm.copy( qa ).slerp( qb, t ); }, slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation var x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ], x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { var s = 1 - t, cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { var sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } var tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } } ); Object.defineProperties( Quaternion.prototype, { x: { get: function () { return this._x; }, set: function ( value ) { this._x = value; this.onChangeCallback(); } }, y: { get: function () { return this._y; }, set: function ( value ) { this._y = value; this.onChangeCallback(); } }, z: { get: function () { return this._z; }, set: function ( value ) { this._z = value; this.onChangeCallback(); } }, w: { get: function () { return this._w; }, set: function ( value ) { this._w = value; this.onChangeCallback(); } } } ); Object.assign( Quaternion.prototype, { isQuaternion: true, set: function ( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._w ); }, copy: function ( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this.onChangeCallback(); return this; }, setFromEuler: function ( euler, update ) { if ( ! ( euler && euler.isEuler ) ) { throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); } var x = euler._x, y = euler._y, z = euler._z, order = euler.order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m var cos = Math.cos; var sin = Math.sin; var c1 = cos( x / 2 ); var c2 = cos( y / 2 ); var c3 = cos( z / 2 ); var s1 = sin( x / 2 ); var s2 = sin( y / 2 ); var s3 = sin( z / 2 ); if ( order === 'XYZ' ) { 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; } else if ( order === 'YXZ' ) { 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; } else if ( order === 'ZXY' ) { 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; } else if ( order === 'ZYX' ) { 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; } else if ( order === 'YZX' ) { 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; } else if ( order === 'XZY' ) { 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; } if ( update !== false ) this.onChangeCallback(); 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 ); this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this.onChangeCallback(); return this; }, setFromUnitVectors: function ( vFrom, vTo ) { // assumes direction vectors vFrom and vTo are normalized var EPS = 0.000001; var r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { this._x = - vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = - vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); }, angleTo: function ( q ) { return 2 * Math.acos( Math.abs( _Math.clamp( this.dot( q ), - 1, 1 ) ) ); }, rotateTowards: function ( q, step ) { var angle = this.angleTo( q ); if ( angle === 0 ) return this; var t = Math.min( 1, step / angle ); this.slerp( q, t ); return this; }, inverse: function () { // quaternion is assumed to have unit length return this.conjugate(); }, conjugate: function () { this._x *= - 1; this._y *= - 1; this._z *= - 1; this.onChangeCallback(); return this; }, dot: function ( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; }, lengthSq: function () { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { var l = this.length(); 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; } this.onChangeCallback(); return this; }, multiply: function ( q, p ) { if ( p !== undefined ) { console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); }, premultiply: function ( q ) { return this.multiplyQuaternions( q, this ); }, 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; this.onChangeCallback(); 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 sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if ( sqrSinHalfTheta <= Number.EPSILON ) { var s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; return this.normalize(); } var sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); 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 ); this.onChangeCallback(); return this; }, equals: function ( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Object.assign( Vector3.prototype, { isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( 'index is out of range: ' + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( 'index is out of range: ' + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiply: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; }, multiplyScalar: function ( scalar ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; }, multiplyVectors: function ( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; }, applyEuler: function () { var quaternion = new Quaternion(); return function applyEuler( euler ) { if ( ! ( euler && euler.isEuler ) ) { console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); } return this.applyQuaternion( quaternion.setFromEuler( euler ) ); }; }(), applyAxisAngle: function () { var quaternion = new Quaternion(); return function applyAxisAngle( axis, angle ) { return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); }; }(), applyMatrix3: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; return this; }, applyQuaternion: function ( q ) { var x = this.x, y = this.y, z = this.z; var qx = q.x, qy = q.y, qz = q.z, 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; }, project: function ( camera ) { return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); }, unproject: function ( camera ) { return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); }, transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; }, clamp: function ( min, max ) { // assumes min < max, componentwise this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, clampScalar: function ( minVal, maxVal ) { this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); return this; }, clampLength: function ( min, max ) { var length = this.length(); return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, // TODO lengthSquared? lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, manhattanLength: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); }, normalize: function () { return this.divideScalar( this.length() || 1 ); }, setLength: function ( length ) { return this.normalize().multiplyScalar( length ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, cross: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); return this.crossVectors( v, w ); } return this.crossVectors( this, v ); }