@google/model-viewer
Version:
Easily display interactive 3D models on the web and in AR!
2,426 lines (1,570 loc) • 1.57 MB
JavaScript
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 );
}