UNPKG

three

Version:

JavaScript 3D library

436 lines (288 loc) 9.46 kB
import { Color } from '../../math/Color.js'; import { Matrix2 } from '../../math/Matrix2.js'; import { Matrix3 } from '../../math/Matrix3.js'; import { Matrix4 } from '../../math/Matrix4.js'; import { Vector2 } from '../../math/Vector2.js'; import { Vector3 } from '../../math/Vector3.js'; import { Vector4 } from '../../math/Vector4.js'; // cyrb53 (c) 2018 bryc (github.com/bryc). License: Public domain. Attribution appreciated. // A fast and simple 64-bit (or 53-bit) string hash function with decent collision resistance. // Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity. // See https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/52171480#52171480 // https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js function cyrb53( value, seed = 0 ) { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; if ( value instanceof Array ) { for ( let i = 0, val; i < value.length; i ++ ) { val = value[ i ]; h1 = Math.imul( h1 ^ val, 2654435761 ); h2 = Math.imul( h2 ^ val, 1597334677 ); } } else { for ( let i = 0, ch; i < value.length; i ++ ) { ch = value.charCodeAt( i ); h1 = Math.imul( h1 ^ ch, 2654435761 ); h2 = Math.imul( h2 ^ ch, 1597334677 ); } } h1 = Math.imul( h1 ^ ( h1 >>> 16 ), 2246822507 ); h1 ^= Math.imul( h2 ^ ( h2 >>> 13 ), 3266489909 ); h2 = Math.imul( h2 ^ ( h2 >>> 16 ), 2246822507 ); h2 ^= Math.imul( h1 ^ ( h1 >>> 13 ), 3266489909 ); return 4294967296 * ( 2097151 & h2 ) + ( h1 >>> 0 ); } /** * Computes a hash for the given string. * * @method * @param {string} str - The string to be hashed. * @return {number} The hash. */ export const hashString = ( str ) => cyrb53( str ); /** * Computes a hash for the given array. * * @method * @param {Array<number>} array - The array to be hashed. * @return {number} The hash. */ export const hashArray = ( array ) => cyrb53( array ); /** * Computes a hash for the given list of parameters. * * @method * @param {...number} params - A list of parameters. * @return {number} The hash. */ export const hash = ( ...params ) => cyrb53( params ); /** * Computes a cache key for the given node. * * @method * @param {Object|Node} object - The object to be hashed. * @param {boolean} [force=false] - Whether to force a cache key computation or not. * @return {number} The hash. */ export function getCacheKey( object, force = false ) { const values = []; if ( object.isNode === true ) { values.push( object.id ); object = object.getSelf(); } for ( const { property, childNode } of getNodeChildren( object ) ) { values.push( cyrb53( property.slice( 0, - 4 ) ), childNode.getCacheKey( force ) ); } return cyrb53( values ); } /** * This generator function can be used to iterate over the node children * of the given object. * * @generator * @param {Object} node - The object to be hashed. * @param {boolean} [toJSON=false] - Whether to return JSON or not. * @yields {Object} A result node holding the property, index (if available) and the child node. */ export function* getNodeChildren( node, toJSON = false ) { for ( const property in node ) { // Ignore private properties. if ( property.startsWith( '_' ) === true ) continue; const object = node[ property ]; if ( Array.isArray( object ) === true ) { for ( let i = 0; i < object.length; i ++ ) { const child = object[ i ]; if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) { yield { property, index: i, childNode: child }; } } } else if ( object && object.isNode === true ) { yield { property, childNode: object }; } else if ( typeof object === 'object' ) { for ( const subProperty in object ) { const child = object[ subProperty ]; if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) { yield { property, index: subProperty, childNode: child }; } } } } } const typeFromLength = /*@__PURE__*/ new Map( [ [ 1, 'float' ], [ 2, 'vec2' ], [ 3, 'vec3' ], [ 4, 'vec4' ], [ 9, 'mat3' ], [ 16, 'mat4' ] ] ); const dataFromObject = /*@__PURE__*/ new WeakMap(); /** * Returns the data type for the given the length. * * @method * @param {number} length - The length. * @return {string} The data type. */ export function getTypeFromLength( length ) { return typeFromLength.get( length ); } /** * Returns the typed array for the given data type. * * @method * @param {string} type - The data type. * @return {TypedArray} The typed array. */ export function getTypedArrayFromType( type ) { // Handle component type for vectors and matrices if ( /[iu]?vec\d/.test( type ) ) { // Handle int vectors if ( type.startsWith( 'ivec' ) ) return Int32Array; // Handle uint vectors if ( type.startsWith( 'uvec' ) ) return Uint32Array; // Default to float vectors return Float32Array; } // Handle matrices (always float) if ( /mat\d/.test( type ) ) return Float32Array; // Basic types if ( /float/.test( type ) ) return Float32Array; if ( /uint/.test( type ) ) return Uint32Array; if ( /int/.test( type ) ) return Int32Array; throw new Error( `THREE.NodeUtils: Unsupported type: ${type}` ); } /** * Returns the length for the given data type. * * @method * @param {string} type - The data type. * @return {number} The length. */ export function getLengthFromType( type ) { if ( /float|int|uint/.test( type ) ) return 1; if ( /vec2/.test( type ) ) return 2; if ( /vec3/.test( type ) ) return 3; if ( /vec4/.test( type ) ) return 4; if ( /mat2/.test( type ) ) return 4; if ( /mat3/.test( type ) ) return 9; if ( /mat4/.test( type ) ) return 16; console.error( 'THREE.TSL: Unsupported type:', type ); } /** * Returns the data type for the given value. * * @method * @param {any} value - The value. * @return {?string} The data type. */ export function getValueType( value ) { if ( value === undefined || value === null ) return null; const typeOf = typeof value; if ( value.isNode === true ) { return 'node'; } else if ( typeOf === 'number' ) { return 'float'; } else if ( typeOf === 'boolean' ) { return 'bool'; } else if ( typeOf === 'string' ) { return 'string'; } else if ( typeOf === 'function' ) { return 'shader'; } else if ( value.isVector2 === true ) { return 'vec2'; } else if ( value.isVector3 === true ) { return 'vec3'; } else if ( value.isVector4 === true ) { return 'vec4'; } else if ( value.isMatrix2 === true ) { return 'mat2'; } else if ( value.isMatrix3 === true ) { return 'mat3'; } else if ( value.isMatrix4 === true ) { return 'mat4'; } else if ( value.isColor === true ) { return 'color'; } else if ( value instanceof ArrayBuffer ) { return 'ArrayBuffer'; } return null; } /** * Returns the value/object for the given data type and parameters. * * @method * @param {string} type - The given type. * @param {...any} params - A parameter list. * @return {any} The value/object. */ export function getValueFromType( type, ...params ) { const last4 = type ? type.slice( - 4 ) : undefined; if ( params.length === 1 ) { // ensure same behaviour as in NodeBuilder.format() if ( last4 === 'vec2' ) params = [ params[ 0 ], params[ 0 ] ]; else if ( last4 === 'vec3' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ] ]; else if ( last4 === 'vec4' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ], params[ 0 ] ]; } if ( type === 'color' ) { return new Color( ...params ); } else if ( last4 === 'vec2' ) { return new Vector2( ...params ); } else if ( last4 === 'vec3' ) { return new Vector3( ...params ); } else if ( last4 === 'vec4' ) { return new Vector4( ...params ); } else if ( last4 === 'mat2' ) { return new Matrix2( ...params ); } else if ( last4 === 'mat3' ) { return new Matrix3( ...params ); } else if ( last4 === 'mat4' ) { return new Matrix4( ...params ); } else if ( type === 'bool' ) { return params[ 0 ] || false; } else if ( ( type === 'float' ) || ( type === 'int' ) || ( type === 'uint' ) ) { return params[ 0 ] || 0; } else if ( type === 'string' ) { return params[ 0 ] || ''; } else if ( type === 'ArrayBuffer' ) { return base64ToArrayBuffer( params[ 0 ] ); } return null; } /** * Gets the object data that can be shared between different rendering steps. * * @param {Object} object - The object to get the data for. * @return {Object} The object data. */ export function getDataFromObject( object ) { let data = dataFromObject.get( object ); if ( data === undefined ) { data = {}; dataFromObject.set( object, data ); } return data; } /** * Converts the given array buffer to a Base64 string. * * @method * @param {ArrayBuffer} arrayBuffer - The array buffer. * @return {string} The Base64 string. */ export function arrayBufferToBase64( arrayBuffer ) { let chars = ''; const array = new Uint8Array( arrayBuffer ); for ( let i = 0; i < array.length; i ++ ) { chars += String.fromCharCode( array[ i ] ); } return btoa( chars ); } /** * Converts the given Base64 string to an array buffer. * * @method * @param {string} base64 - The Base64 string. * @return {ArrayBuffer} The array buffer. */ export function base64ToArrayBuffer( base64 ) { return Uint8Array.from( atob( base64 ), c => c.charCodeAt( 0 ) ).buffer; }