UNPKG

3dmol

Version:

Object oriented Javascript molecular visualization library

1,569 lines (1,413 loc) 61.5 kB
(function (global, factory) { (factory((global['MMTF'] = global.MMTF || {}))); }(this, function (exports) { 'use strict'; /** * @file utf8-utils * @private * @author Alexander Rose <alexander.rose@weirdbyte.de> * mostly copied from https://github.com/creationix/msgpack-js-browser * by Tim Caswell <tim@creationix.com>, MIT License, Copyright (c) 2013 */ // Encode string as utf8 into dataview at offset function utf8Write(view, offset, string) { var byteLength = view.byteLength; for(var i = 0, l = string.length; i < l; i++) { var codePoint = string.charCodeAt(i); // One byte of UTF-8 if (codePoint < 0x80) { view.setUint8(offset++, codePoint >>> 0 & 0x7f | 0x00); continue; } // Two bytes of UTF-8 if (codePoint < 0x800) { view.setUint8(offset++, codePoint >>> 6 & 0x1f | 0xc0); view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); continue; } // Three bytes of UTF-8. if (codePoint < 0x10000) { view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0); view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); continue; } // Four bytes of UTF-8 if (codePoint < 0x110000) { view.setUint8(offset++, codePoint >>> 18 & 0x07 | 0xf0); view.setUint8(offset++, codePoint >>> 12 & 0x3f | 0x80); view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); continue; } throw new Error("bad codepoint " + codePoint); } } function utf8ByteCount(string) { var count = 0; for(var i = 0, l = string.length; i < l; i++) { var codePoint = string.charCodeAt(i); if (codePoint < 0x80) { count += 1; continue; } if (codePoint < 0x800) { count += 2; continue; } if (codePoint < 0x10000) { count += 3; continue; } if (codePoint < 0x110000) { count += 4; continue; } throw new Error("bad codepoint " + codePoint); } return count; } /** * encode data value (recursively) into binary encoded MessagePack v5 (http://msgpack.org/) * @param {Object|Array|String|Number|Boolean|null} value [description] * @param {DataView} view [description] * @param {Integer} offset [description] * @return {Integer} number of bytes written into view */ function encode$1(value, view, offset) { var type = typeof value; // Strings Bytes if (type === "string") { var length = utf8ByteCount(value); // fix str if (length < 0x20) { view.setUint8(offset, length | 0xa0); utf8Write(view, offset + 1, value); return 1 + length; } // str 8 if (length < 0x100) { view.setUint8(offset, 0xd9); view.setUint8(offset + 1, length); utf8Write(view, offset + 2, value); return 2 + length; } // str 16 if (length < 0x10000) { view.setUint8(offset, 0xda); view.setUint16(offset + 1, length); utf8Write(view, offset + 3, value); return 3 + length; } // str 32 if (length < 0x100000000) { view.setUint8(offset, 0xdb); view.setUint32(offset + 1, length); utf8Write(view, offset + 5, value); return 5 + length; } } if (value instanceof Uint8Array) { var length = value.byteLength; var bytes = new Uint8Array(view.buffer); // bin 8 if (length < 0x100) { view.setUint8(offset, 0xc4); view.setUint8(offset + 1, length); bytes.set(value, offset + 2); return 2 + length; } // bin 16 if (length < 0x10000) { view.setUint8(offset, 0xc5); view.setUint16(offset + 1, length); bytes.set(value, offset + 3); return 3 + length; } // bin 32 if (length < 0x100000000) { view.setUint8(offset, 0xc6); view.setUint32(offset + 1, length); bytes.set(value, offset + 5); return 5 + length; } } if (type === "number") { if (!isFinite(value)) { throw new Error("Number not finite: " + value); } // Floating point if (Math.floor(value) !== value) { view.setUint8(offset, 0xcb); view.setFloat64(offset + 1, value); return 9; } // Integers if (value >=0) { // positive fixnum if (value < 0x80) { view.setUint8(offset, value); return 1; } // uint 8 if (value < 0x100) { view.setUint8(offset, 0xcc); view.setUint8(offset + 1, value); return 2; } // uint 16 if (value < 0x10000) { view.setUint8(offset, 0xcd); view.setUint16(offset + 1, value); return 3; } // uint 32 if (value < 0x100000000) { view.setUint8(offset, 0xce); view.setUint32(offset + 1, value); return 5; } throw new Error("Number too big 0x" + value.toString(16)); } // negative fixnum if (value >= -0x20) { view.setInt8(offset, value); return 1; } // int 8 if (value >= -0x80) { view.setUint8(offset, 0xd0); view.setInt8(offset + 1, value); return 2; } // int 16 if (value >= -0x8000) { view.setUint8(offset, 0xd1); view.setInt16(offset + 1, value); return 3; } // int 32 if (value >= -0x80000000) { view.setUint8(offset, 0xd2); view.setInt32(offset + 1, value); return 5; } throw new Error("Number too small -0x" + (-value).toString(16).substr(1)); } // null if (value === null) { view.setUint8(offset, 0xc0); return 1; } // Boolean if (type === "boolean") { view.setUint8(offset, value ? 0xc3 : 0xc2); return 1; } // Container Types if (type === "object") { var length, size = 0; var isArray = Array.isArray(value); if (isArray) { length = value.length; } else { var keys = Object.keys(value); length = keys.length; } var size; if (length < 0x10) { view.setUint8(offset, length | (isArray ? 0x90 : 0x80)); size = 1; } else if (length < 0x10000) { view.setUint8(offset, isArray ? 0xdc : 0xde); view.setUint16(offset + 1, length); size = 3; } else if (length < 0x100000000) { view.setUint8(offset, isArray ? 0xdd : 0xdf); view.setUint32(offset + 1, length); size = 5; } if (isArray) { for (var i = 0; i < length; i++) { size += encode$1(value[i], view, offset + size); } } else { for (var i = 0; i < length; i++) { var key = keys[i]; size += encode$1(key, view, offset + size); size += encode$1(value[key], view, offset + size); } } return size; } throw new Error("Unknown type " + type); } function encodedSize(value) { var type = typeof value; // Raw Bytes if (type === "string") { var length = utf8ByteCount(value); if (length < 0x20) { return 1 + length; } if (length < 0x100) { return 2 + length; } if (length < 0x10000) { return 3 + length; } if (length < 0x100000000) { return 5 + length; } } if (value instanceof Uint8Array) { var length = value.byteLength; if (length < 0x100) { return 2 + length; } if (length < 0x10000) { return 3 + length; } if (length < 0x100000000) { return 5 + length; } } if (type === "number") { // Floating Point // double if (Math.floor(value) !== value) return 9; // Integers if (value >=0) { // positive fixnum if (value < 0x80) return 1; // uint 8 if (value < 0x100) return 2; // uint 16 if (value < 0x10000) return 3; // uint 32 if (value < 0x100000000) return 5; throw new Error("Number too big 0x" + value.toString(16)); } // negative fixnum if (value >= -0x20) return 1; // int 8 if (value >= -0x80) return 2; // int 16 if (value >= -0x8000) return 3; // int 32 if (value >= -0x80000000) return 5; throw new Error("Number too small -0x" + value.toString(16).substr(1)); } // Boolean, null if (type === "boolean" || value === null) return 1; // Container Types if (type === "object") { var length, size = 0; if (Array.isArray(value)) { length = value.length; for (var i = 0; i < length; i++) { size += encodedSize(value[i]); } } else { var keys = Object.keys(value); length = keys.length; for (var i = 0; i < length; i++) { var key = keys[i]; size += encodedSize(key) + encodedSize(value[key]); } } if (length < 0x10) { return 1 + size; } if (length < 0x10000) { return 3 + size; } if (length < 0x100000000) { return 5 + size; } throw new Error("Array or object too long 0x" + length.toString(16)); } throw new Error("Unknown type " + type); } function encodeMsgpack(value) { var buffer = new ArrayBuffer(encodedSize(value)); var view = new DataView(buffer); encode$1(value, view, 0); return new Uint8Array(buffer); } /** * @file mmtf-constants * @private * @author Alexander Rose <alexander.rose@weirdbyte.de> */ var PassThroughFields = [ "mmtfVersion", "mmtfProducer", "unitCell", "spaceGroup", "structureId", "title", "depositionDate", "releaseDate", "experimentalMethods", "resolution", "rFree", "rWork", "bioAssemblyList", "ncsOperatorList", "entityList", "groupList", "numBonds", "numAtoms", "numGroups", "numChains", "numModels", "groupsPerChain", "chainsPerModel", ]; var EncodedFields = [ // required "xCoordList", "yCoordList", "zCoordList", "groupIdList", "groupTypeList", "chainIdList", // optional "bFactorList", "atomIdList", "altLocList", "occupancyList", "secStructList", "insCodeList", "sequenceIndexList", "chainNameList", "bondAtomList", "bondOrderList" ]; var AllFields = PassThroughFields.concat( EncodedFields ); /** * @file mmtf-utils * @private * @author Alexander Rose <alexander.rose@weirdbyte.de> */ /** * mmtf utils module. * @module MmtfUtils */ function getView( ctor, typedArray, elemSize ){ return typedArray ? new ctor( typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / ( elemSize || 1 ) ) : undefined; } function getDataView( typedArray ){ return getView( DataView, typedArray ); } /** * get an Uint8Array view on the input array memory * @static * @param {TypedArray} dataArray - input array * @return {Uint8Array} new view on the input array memory */ function getUint8View( typedArray ){ return getView( Uint8Array, typedArray ); } /** * get an Int8Array view on the input array memory * @static * @param {TypedArray} dataArray - input array * @return {Int8Array} new view on the input array memory */ function getInt8View( typedArray ){ return getView( Int8Array, typedArray ); } /** * get an Int32Array view on the input array memory * @static * @param {TypedArray} dataArray - input array * @return {Int32Array} new view on the input array memory */ function getInt32View( typedArray ){ return getView( Int32Array, typedArray, 4 ); } function getFloat32View( typedArray ){ return getView( Float32Array, typedArray, 4 ); } /** * get an Int16Array copy of the the input array data * @static * @param {TypedArray} view - input data in big endian format * @param {Int16Array} [dataArray] - pre-allocated output array * @return {Int16Array} copy of the input array data */ function decodeInt16( bytes, output ){ var n = bytes.length / 2; if( !output ) output = new Int16Array( n ); for( var i = 0, i2 = 0; i < n; ++i, i2 += 2 ){ output[ i ] = bytes[ i2 ] << 8 ^ bytes[ i2 + 1 ] << 0; } return output; } /** * make big endian buffer of an int16 array * @static * @param {Array|TypedArray} array - array of int16 values * @return {ArrayBuffer} big endian buffer */ function encodeInt16( array, output ){ var n = array.length; if( !output ) output = new Uint8Array( 2 * n ); var dv = getDataView( output ); for( var i = 0; i < n; ++i ){ dv.setInt16( 2 * i, array[ i ] ); } return getUint8View( output ); } /** * get an Int32Array copy of the the input array data * @static * @param {TypedArray} view - input data in big endian format * @param {Int32Array} [dataArray] - pre-allocated output array * @return {Int32Array} copy of the input array data */ function decodeInt32( bytes, output ){ var n = bytes.length / 4; if( !output ) output = new Int32Array( n ); for( var i = 0, i4 = 0; i < n; ++i, i4 += 4 ){ output[ i ] = ( bytes[ i4 ] << 24 ^ bytes[ i4 + 1 ] << 16 ^ bytes[ i4 + 2 ] << 8 ^ bytes[ i4 + 3 ] << 0 ); } return output; } /** * make big endian buffer of an int32 array * @static * @param {Array|TypedArray} array - array of int32 values * @return {ArrayBuffer} big endian buffer */ function encodeInt32( array, output ){ var n = array.length; if( !output ) output = new Uint8Array( 4 * n ); var dv = getDataView( output ); for( var i = 0; i < n; ++i ){ dv.setInt32( 4 * i, array[ i ] ); } return getUint8View( output ); } function decodeFloat32( bytes, output ){ var n = bytes.length; if( !output ) output = new Float32Array( n / 4 ); var dvOut = getDataView( output ); var dvIn = getDataView( bytes ); for( var i = 0, i4 = 0, il = n / 4; i < il; ++i, i4 += 4 ){ dvOut.setFloat32( i4, dvIn.getFloat32( i4 ), true ); } return output; } /** * decode integers into floats using given divisor * example: * intArray: [ 12, 34, 543, 687, 2, 0, 4689 ] * divisor: 100 * return: [ 0.12, 0.34, 5.43, 6.87, 0.02, 0.00, 46.89 ] * @static * @param {TypedArray|Array} intArray - input array containing integers * @param {Number} divisor - number to devide the integers to obtain floats * @param {Float32Array} [dataArray] - pre-allocated output array * @return {Float32Array} decoded array */ function decodeInteger( intArray, divisor, output ){ var n = intArray.length; var invDiv = 1/divisor; if( !output ) output = new Float32Array( n ); for( var i = 0; i < n; ++i ){ // multiply by inverse of the divisor which is faster then division output[ i ] = intArray[ i ] * invDiv; } return output; } function encodeInteger( floatArray, factor, output ){ var n = floatArray.length; if( !output ) output = new Int32Array( n ); for( var i = 0; i < n; ++i ){ output[ i ] = Math.round( floatArray[ i ] * factor ); } return output; } /** * perform run-length decoding of input array * example: * array: [ 0, 2, 3, 5 ] // pairs of values and length of a run * return: [ 0, 0, 3, 3, 3, 3, 3 ] * @static * @param {TypedArray|Array} array - run-length encoded input array * @param {TypedArray|Array} [dataArray] - pre-allocated output array * @return {TypedArray|Array} decoded array */ function decodeRun( array, output ){ var i, il; if( !output ){ // calculate the length the decoded array will have var fullLength = 0; for( i = 0, il = array.length; i < il; i+=2 ){ fullLength += array[ i + 1 ]; } // create a new array of the same type of the input array output = new array.constructor( fullLength ); } var dataOffset = 0; for( i = 0, il = array.length; i < il; i+=2 ){ var value = array[ i ]; // value to be repeated var length = array[ i + 1 ]; // number of repeats for( var j = 0; j < length; ++j ){ output[ dataOffset ] = value; ++dataOffset; } } return output; } function encodeRun( array ){ if( array.length === 0 ) return new Int32Array(); var i, il; // calculate output size var fullLength = 2; for( i = 1, il = array.length; i < il; ++i ){ if( array[ i - 1 ] !== array[ i ] ){ fullLength += 2; } } var output = new Int32Array( fullLength ); var offset = 0; var runLength = 1; for( i = 1, il = array.length; i < il; ++i ){ if( array[ i - 1 ] !== array[ i ] ){ output[ offset ] = array[ i - 1 ]; output[ offset + 1 ] = runLength; runLength = 1; offset += 2; }else{ ++runLength; } } output[ offset ] = array[ array.length - 1 ]; output[ offset + 1 ] = runLength; return output; } /** * perform delta decoding of the input array * by iterativly adding the ith element's value to the i+1th * example: * dataArray: [ 0, 2, 1, 2, 1, 1, -4, -2, 9 ] * return: [ 0, 2, 3, 5, 6, 7, 3, 1, 10 ] * @static * @param {TypedArray|Array} dataArray - delta encoded input array * @return {TypedArray|Array} decoded array */ function decodeDelta( array, output ){ var n = array.length; if( !output ) output = new array.constructor( n ); if( n ) output[ 0 ] = array[ 0 ]; for( var i = 1; i < n; ++i ){ output[ i ] = array[ i ] + output[ i - 1 ]; } return output; } function encodeDelta( array, output ){ var n = array.length; if( !output ) output = new array.constructor( n ); output[ 0 ] = array[ 0 ]; for( var i = 1; i < n; ++i ){ output[ i ] = array[ i ] - array[ i - 1 ]; } return output; } /** * [decodePacking description] * @param {Int16Array|Int8Array} int16or8 [description] * @param {Int32Array} output [description] * @return {Int32Array} [description] */ function decodePacking( int16or8, output ){ var upperLimit = int16or8 instanceof Int8Array ? 0x7F : 0x7FFF; var lowerLimit = -upperLimit - 1; var n = int16or8.length; var i, j; if( !output ){ var fullLength = 0; for( i = 0; i < n; ++i ){ if( int16or8[ i ] < upperLimit && int16or8[ i ] > lowerLimit ){ ++fullLength; } } output = new Int32Array( fullLength ); } i = 0; j = 0; while( i < n ){ var value = 0; while( int16or8[ i ] === upperLimit || int16or8[ i ] === lowerLimit ){ value += int16or8[ i ]; ++i; } value += int16or8[ i ]; ++i; output[ j ] = value; ++j; } return output; } /** * integer packing using recursive indexing * @param {Array|TyepedArray} intArray [description] * @param {Boolean} useInt8 [description] * @return {Int16Array|Int8Array} [description] */ function encodePacking( intArray, useInt8 ){ var upperLimit = useInt8 ? 0x7F : 0x7FFF; var lowerLimit = -upperLimit - 1; var i; var n = intArray.length; var size = 0; for( i = 0; i < n; ++i ){ var value = intArray[ i ]; if( value === 0 ){ ++size; }else if( value === upperLimit || value === lowerLimit ){ size += 2; }else if( value > 0) { size += Math.ceil( value / upperLimit ); }else { size += Math.ceil( value / lowerLimit ); } } var output = useInt8 ? new Int8Array( size ) : new Int16Array( size ); var j = 0; for( i = 0; i < n; ++i ){ var value = intArray[ i ]; if( value >= 0) { while( value >= upperLimit ){ output[ j ] = upperLimit; ++j; value -= upperLimit; } }else{ while( value <= lowerLimit ){ output[ j ] = lowerLimit; ++j; value -= lowerLimit; } } output[ j ] = value; ++j; } return output; } function decodeDeltaRun( array, output ){ return decodeDelta( decodeRun( array ), output ); } function encodeDeltaRun( array ){ return encodeRun( encodeDelta( array ) ); } /** * perform run-length decoding followed (@see decodeRunLength) * by decoding integers into floats using given divisor (@see decodeIntegerToFloat) * example: * array: [ 320, 3, 100, 2 ] * divisor: 100 * return: [ 3.20, 3.20, 3.20, 1.00, 1.00 ] * @static * @param {Uint8Array} array - run-length encoded int32 array as bytes in big endian format * @param {Integer} divisor - number to devide the integers to obtain floats * @param {Float32Array} dataArray - pre-allocated output array * @return {Float32Array} decoded array */ function decodeIntegerRun( intArray, divisor, output ){ return decodeInteger( decodeRun( intArray, getInt32View( output ) ), divisor, output ); } function encodeIntegerRun( floatArray, factor ){ return encodeRun( encodeInteger( floatArray, factor ) ); } function decodeIntegerDelta( intArray, divisor, output ){ return decodeInteger( decodeDelta( intArray, getInt32View( output ) ), divisor, output ); } function encodeIntegerDelta( floatArray, factor, output ){ return encodeDelta( encodeInteger( floatArray, factor ), output ); } function decodeIntegerPacking( int16or8, divisor, output ){ return decodeInteger( decodePacking( int16or8, getInt32View( output ) ), divisor, output ); } function decodeIntegerDeltaPacking( int16or8, divisor, output ){ var unpacked = decodePacking( int16or8, getInt32View( output ) ); return decodeIntegerDelta( unpacked, divisor, getFloat32View( unpacked ) ); } function encodeIntegerDeltaPacking( floatArray, factor, useInt8 ){ return encodePacking( encodeIntegerDelta( floatArray, factor ), useInt8 ); } function decodeBytes( bytes ){ var dv = getDataView( bytes ); var type = dv.getInt32( 0 ); var size = dv.getInt32( 4 ); var param = bytes.subarray( 8, 12 ); var bytes = bytes.subarray( 12 ); return [ type, bytes, size, param ]; } function encodeBytes( type, size, param, bytes ){ var buffer = new ArrayBuffer( 12 + bytes.byteLength ); var out = new Uint8Array( buffer ); var dv = new DataView( buffer ); dv.setInt32( 0, type ); dv.setInt32( 4, size ); if( param ) out.set( param, 8 ); out.set( bytes, 12 ); return out; } function passInt8( int8 ){ var size = int8.length; var bytes = getUint8View( int8 ); return encodeBytes( 2, size, undefined, bytes ); } function passInt32( int32 ){ var size = int32.length; var bytes = encodeInt32( int32 ); return encodeBytes( 4, size, undefined, bytes ); } function passString( stringBytes, length ){ var size = stringBytes.length / length; var param = encodeInt32([ length ]); var bytes = getUint8View( stringBytes ); return encodeBytes( 5, size, param, bytes ); } function runChar( charBytes ){ var size = charBytes.length; var bytes = encodeInt32( encodeRun( charBytes ) ); return encodeBytes( 6, size, undefined, bytes ); } function deltaRun( int32 ){ var size = int32.length; var bytes = encodeInt32( encodeDeltaRun( int32 ) ); return encodeBytes( 8, size, undefined, bytes ); } function integerRun( float32, factor ){ var size = float32.length; var param = encodeInt32([ factor ]); var bytes = encodeInt32( encodeIntegerRun( float32, factor ) ); return encodeBytes( 9, size, param, bytes ); } function integerDeltaPacking16( float32, factor ){ var size = float32.length; var param = encodeInt32([ factor ]); var bytes = encodeInt16( encodeIntegerDeltaPacking( float32, factor ) ); return encodeBytes( 10, size, param, bytes ); } function encodeMmtf( inputDict ){ var outputDict = {}; // copy some fields over from the input dict PassThroughFields.forEach( function( name ){ if( inputDict[ name ] !== undefined ){ outputDict[ name ] = inputDict[ name ]; } } ); ////////////// // bond data // encode inter group bond atom indices, i.e. get bytes in big endian order if( inputDict.bondAtomList ){ outputDict.bondAtomList = passInt32( inputDict.bondAtomList ); } // encode inter group bond orders, i.e. get bytes if( inputDict.bondOrderList ){ outputDict.bondOrderList = passInt8( inputDict.bondOrderList ); } ////////////// // atom data // split-list delta & integer encode x, y, z atom coords outputDict.xCoordList = integerDeltaPacking16( inputDict.xCoordList, 1000 ); outputDict.yCoordList = integerDeltaPacking16( inputDict.yCoordList, 1000 ); outputDict.zCoordList = integerDeltaPacking16( inputDict.zCoordList, 1000 ); // split-list delta & integer encode b-factors if( inputDict.bFactorList ){ outputDict.bFactorList = integerDeltaPacking16( inputDict.bFactorList, 100 ); } // delta & run-length encode atom ids if( inputDict.atomIdList ){ outputDict.atomIdList = deltaRun( inputDict.atomIdList ); } // run-length encode alternate labels if( inputDict.altLocList ){ outputDict.altLocList = runChar( inputDict.altLocList ); } // run-length & integer encode occupancies if( inputDict.occupancyList ){ outputDict.occupancyList = integerRun( inputDict.occupancyList, 100 ); } /////////////// // group data // run-length & delta encode group numbers outputDict.groupIdList = deltaRun( inputDict.groupIdList ); // encode group types, i.e. get int32 array outputDict.groupTypeList = passInt32( inputDict.groupTypeList ); // encode secondary structure, i.e. get bytes if( inputDict.secStructList ){ outputDict.secStructList = passInt8( inputDict.secStructList ); } // run-length encode insertion codes if( inputDict.insCodeList ){ outputDict.insCodeList = runChar( inputDict.insCodeList ); } // run-length & delta encode sequence indices if( inputDict.sequenceIndexList ){ outputDict.sequenceIndexList = deltaRun( inputDict.sequenceIndexList ); } /////////////// // chain data // encode chain ids, i.e. get bytes outputDict.chainIdList = passString( inputDict.chainIdList, 4 ); // encode chain names, i.e. get bytes if( inputDict.chainNameList ){ outputDict.chainNameList = passString( inputDict.chainNameList, 4 ); } return outputDict; } /** * @file msgpack-decode * @private * @author Alexander Rose <alexander.rose@weirdbyte.de> */ /** * msgpack decode module. * @module MsgpackDecode */ /** * decode binary encoded MessagePack v5 (http://msgpack.org/) data * @static * @param {Uint8Array} buffer - binary encoded MessagePack data * @return {Object|Array|String|Number|Boolean|null} decoded Messagepack data */ function decodeMsgpack(buffer) { // Loosely based on // The MIT License (MIT) // Copyright (c) 2013 Tim Caswell <tim@creationix.com> // https://github.com/creationix/msgpack-js var offset = 0; var dataView = new DataView(buffer.buffer); /** * decode all key-value pairs of a map into an object * @param {Integer} length - number of key-value pairs * @return {Object} decoded map */ function map(length) { var value = {}; for (var i = 0; i < length; i++) { var key = parse(); value[key] = parse(); } return value; } /** * decode binary array * @param {Integer} length - number of elements in the array * @return {Uint8Array} decoded array */ function bin(length) { var value = buffer.subarray(offset, offset + length); offset += length; return value; } /** * decode string * @param {Integer} length - number string characters * @return {String} decoded string */ function str(length) { var array = buffer.subarray(offset, offset + length); offset += length; // limit number of arguments to String.fromCharCode to something // browsers can handle, see http://stackoverflow.com/a/22747272 var chunkSize = 0xffff; if(length > chunkSize){ var c = []; for(var i = 0; i < array.length; i += chunkSize) { c.push(String.fromCharCode.apply( null, array.subarray(i, i + chunkSize) )); } return c.join(""); }else{ return String.fromCharCode.apply(null, array); } } /** * decode array * @param {Integer} length - number of array elements * @return {Array} decoded array */ function array(length) { var value = new Array(length); for (var i = 0; i < length; i++) { value[i] = parse(); } return value; } /** * recursively parse the MessagePack data * @return {Object|Array|String|Number|Boolean|null} decoded MessagePack data */ function parse() { var type = buffer[offset]; var value, length, extType; // Positive FixInt if ((type & 0x80) === 0x00) { offset++; return type; } // FixMap if ((type & 0xf0) === 0x80) { length = type & 0x0f; offset++; return map(length); } // FixArray if ((type & 0xf0) === 0x90) { length = type & 0x0f; offset++; return array(length); } // FixStr if ((type & 0xe0) === 0xa0) { length = type & 0x1f; offset++; return str(length); } // Negative FixInt if ((type & 0xe0) === 0xe0) { value = dataView.getInt8(offset); offset++; return value; } switch (type) { // nil case 0xc0: offset++; return null; // 0xc1: (never used, could be employed for padding) // false case 0xc2: offset++; return false; // true case 0xc3: offset++; return true; // bin 8 case 0xc4: length = dataView.getUint8(offset + 1); offset += 2; return bin(length); // bin 16 case 0xc5: length = dataView.getUint16(offset + 1); offset += 3; return bin(length); // bin 32 case 0xc6: length = dataView.getUint32(offset + 1); offset += 5; return bin(length); // // ext 8 // case 0xc7: // length = dataView.getUint8(offset + 1); // extType = dataView.getUint8(offset + 2); // offset += 3; // return [extType, bin(length)]; // // ext 16 // case 0xc8: // length = dataView.getUint16(offset + 1); // extType = dataView.getUint8(offset + 3); // offset += 4; // return [extType, bin(length)]; // // ext 32 // case 0xc9: // length = dataView.getUint32(offset + 1); // extType = dataView.getUint8(offset + 5); // offset += 6; // return [extType, bin(length)]; // float 32 case 0xca: value = dataView.getFloat32(offset + 1); offset += 5; return value; // float 64 case 0xcb: value = dataView.getFloat64(offset + 1); offset += 9; return value; // uint8 case 0xcc: value = buffer[offset + 1]; offset += 2; return value; // uint 16 case 0xcd: value = dataView.getUint16(offset + 1); offset += 3; return value; // uint 32 case 0xce: value = dataView.getUint32(offset + 1); offset += 5; return value; // // uint64 // case 0xcf: // // FIXME not available/representable in JS // // largest possible int in JS is 2^53 // // value = dataView.getUint64(offset + 1); // offset += 9; // return 0; // int 8 case 0xd0: value = dataView.getInt8(offset + 1); offset += 2; return value; // int 16 case 0xd1: value = dataView.getInt16(offset + 1); offset += 3; return value; // int 32 case 0xd2: value = dataView.getInt32(offset + 1); offset += 5; return value; // // int 64 // case 0xd3: // // FIXME not available/representable in JS // // largest possible int in JS is 2^53 // // value = dataView.getInt64(offset + 1); // offset += 9; // return 0; // // fixext 1 // case 0xd4: // extType = dataView.getUint8(offset + 1); // offset += 2; // return [extType, bin(1)]; // // fixext 2 // case 0xd5: // extType = dataView.getUint8(offset + 1); // offset += 2; // return [extType, bin(2)]; // // fixext 4 // case 0xd6: // extType = dataView.getUint8(offset + 1); // offset += 2; // return [extType, bin(4)]; // // fixext 8 // case 0xd7: // extType = dataView.getUint8(offset + 1); // offset += 2; // return [extType, bin(8)]; // // fixext 16 // case 0xd8: // extType = dataView.getUint8(offset + 1); // offset += 2; // return [extType, bin(16)]; // str 8 case 0xd9: length = dataView.getUint8(offset + 1); offset += 2; return str(length); // str 16 case 0xda: length = dataView.getUint16(offset + 1); offset += 3; return str(length); // str 32 case 0xdb: length = dataView.getUint32(offset + 1); offset += 5; return str(length); // array 16 case 0xdc: length = dataView.getUint16(offset + 1); offset += 3; return array(length); // array 32 case 0xdd: length = dataView.getUint32(offset + 1); offset += 5; return array(length); // map 16: case 0xde: length = dataView.getUint16(offset + 1); offset += 3; return map(length); // map 32 case 0xdf: length = dataView.getUint32(offset + 1); offset += 5; return map(length); } throw new Error("Unknown type 0x" + type.toString(16)); } // start the recursive parsing return parse(); } /** * Fields shared in encoded and decoded mmtf data objects. * @typedef {Object} module:MmtfDecode.SharedMmtfData * @property {String} mmtfVersion - MMTF specification version * @property {String} mmtfProducer - Program that created the file * @property {Float[]} [unitCell] - Crystallographic unit cell * @property {Float} unitCell.0 - x length * @property {Float} unitCell.1 - y length * @property {Float} unitCell.2 - z length * @property {Float} unitCell.3 - alpha angle * @property {Float} unitCell.4 - beta angle * @property {Float} unitCell.5 - gamma angle * @property {String} [spaceGroup] - Hermann-Mauguin symbol * @property {String} [structureId] - Some reference, e.g. a PDB ID * @property {String} [title] - Short description * @property {String} [depositionDate] - Deposition date in YYYY-MM-DD format * @property {String} [releaseDate] - Release date in YYYY-MM-DD format * @property {String[]} [experimentalMethods] - Structure determination methods * @property {Float} [resolution] - Resolution in Å * @property {Float} [rFree] - R-free value * @property {Float} [rWork] - R-work value * @property {Integer} numBonds - Number of bonds * @property {Integer} numAtoms - Number of atoms * @property {Integer} numGroups - Number of groups (residues) * @property {Integer} numChains - Number of chains * @property {Integer} numModels - Number of models * @property {Integer[]} chainsPerModel - List of number of chains in each model * @property {Integer[]} groupsPerChain - List of number of groups in each chain * @property {Entity[]} [entityList] - List of entity objects * @property {Integer[]} entityList.chainIndexList - Pointers into chain data fields * @property {String} entityList.description - Description of the entity * @property {String} entityList.type - Name of the entity type * @property {String} entityList.sequence - One letter code sequence * @property {Assembly[]} [bioAssemblyList] - List of assembly objects * @property {Transform[]} bioAssemblyList.transformList - List of transform objects * @property {Integer[]} bioAssemblyList.transformList.chainIndexList - Pointers into chain data fields * @property {Float[]} bioAssemblyList.transformList.matrix - 4x4 transformation matrix * @property {Array[]} [ncsOperatorList] - List of ncs operator matrices * @property {Float[]} ncsOperatorList. - 4x4 transformation matrix * @property {GroupType[]} groupList - List of groupType objects * @property {Integer[]} groupList.formalChargeList - List of atom formal charges * @property {String[]} groupList.elementList - List of elements * @property {String[]} groupList.atomNameList - List of atom names * @property {Integer[]} groupList.bondAtomList - List of bonded atom indices * @property {Integer[]} groupList.bondOrderList - List of bond orders * @property {String} groupList.groupName - The name of the group * @property {String} groupList.singleLetterCode - The single letter code * @property {String} groupList.chemCompType - The chemical component type */ /** * Encoded mmtf data object. Also includes the fields from {@link module:MmtfDecode.SharedMmtfData}. See MMTF specification on how they are encoded. * @typedef {Object} module:MmtfDecode.EncodedMmtfData * @mixes module:MmtfDecode.SharedMmtfData * @property {Uint8Array} [bondAtomList] - Encoded bonded atom indices * @property {Uint8Array} [bondOrderList] - Encoded bond orders * @property {Uint8Array} xCoordBig - Encoded x coordinates in Å, part 1 * @property {Uint8Array} xCoordSmall - Encoded x coordinates in Å, part 2 * @property {Uint8Array} yCoordBig - Encoded y coordinates in Å, part 1 * @property {Uint8Array} yCoordSmall - Encoded y coordinates in Å, part 2 * @property {Uint8Array} yCoordBig - Encoded y coordinates in Å, part 1 * @property {Uint8Array} yCoordSmall - Encoded y coordinates in Å, part 2 * @property {Uint8Array} [bFactorBig] - Encoded B-factors in Å^2, part 1 * @property {Uint8Array} [bFactorSmall] - Encoded B-factors in Å^2, part 2 * @property {Uint8Array} [atomIdList] - Encoded atom ids * @property {Uint8Array} [altLocList] - Encoded alternate location labels * @property {Uint8Array} [occupancyList] - Encoded occupancies * @property {Uint8Array} groupIdList - Encoded group ids * @property {Uint8Array} groupTypeList - Encoded group types * @property {Uint8Array} [secStructList] - Encoded secondary structure codes * @property {Uint8Array} [insCodeList] - Encoded insertion codes * @property {Uint8Array} [seuenceIdList] - Encoded sequence ids * @property {Uint8Array} chainIdList - Encoded chain ids * @property {Uint8Array} [chainNameList] - Encoded chain names */ /** * Decoded mmtf data object. Also includes fields the from {@link module:MmtfDecode.SharedMmtfData}. * @typedef {Object} module:MmtfDecode.MmtfData * @mixes module:MmtfDecode.SharedMmtfData * @property {Int32Array} [bondAtomList] - List of bonded atom indices * @property {Uint8Array} [bondOrderList] - List of bond orders * @property {Float32Array} xCoordList - List of x coordinates in Å * @property {Float32Array} yCoordList - List of y coordinates in Å * @property {Float32Array} zCoordList - List of z coordinates in Å * @property {Float32Array} [bFactorList] - List of B-factors in Å^2 * @property {Int32Array} [atomIdList] - List of atom ids * @property {Uint8Array} [altLocList] - List of alternate location labels * @property {Float32Array} [occupancyList] - List of occupancies * @property {Int32Array} groupIdList - List of group ids * @property {Int32Array} groupTypeList - List of group types * @property {Int8Array} [secStructList] - List of secondary structure codes, encoding * 0: pi helix, 1: bend, 2: alpha helix, 3: extended, * 4: 3-10 helix, 5: bridge, 6: turn, 7: coil, -1: undefined * @property {Uint8Array} [insCodeList] - List of insertion codes * @property {Int32Array} [seuenceIdList] - List of sequence ids * @property {Uint8Array} chainIdList - List of chain ids * @property {Uint8Array} [chainNameList] - List of chain names */ /** * [performDecoding description] * @param {Integer} bytes [description] * @param {Integer} size [description] * @param {Uint8Array} param [description] * @return {TypedArray} [description] */ function performDecoding( type, bytes, size, param ){ switch( type ){ case 1: return decodeFloat32( bytes ); case 2: return getInt8View( bytes ); case 3: return decodeInt16( bytes ); case 4: return decodeInt32( bytes ); case 5: // var length = decodeInt32( param )[ 0 ]; return getUint8View( bytes ); // interpret as string array case 6: // interpret as char array return decodeRun( decodeInt32( bytes ), new Uint8Array( size ) ); case 7: return decodeRun( decodeInt32( bytes ) ) case 8: return decodeDeltaRun( decodeInt32( bytes ) ); case 9: return decodeIntegerRun( decodeInt32( bytes ), decodeInt32( param )[ 0 ] ); case 10: return decodeIntegerDeltaPacking( decodeInt16( bytes ), decodeInt32( param )[ 0 ] ); case 11: return decodeInteger( decodeInt16( bytes ), decodeInt32( param )[ 0 ] ); case 12: return decodeIntegerPacking( decodeInt16( bytes ), decodeInt32( param )[ 0 ] ); case 13: return decodeIntegerPacking( getInt8View( bytes ), decodeInt32( param )[ 0 ] ); case 14: return decodePacking( decodeInt16( bytes ) ); case 15: return decodePacking( getInt8View( bytes ) ); } }; /** * Decode MMTF fields * @static * @param {Object} inputDict - encoded MMTF data * @param {Object} [params] - decoding parameters * @param {String[]} params.ignoreFields - names of optional fields not to decode * @return {module:MmtfDecode.MmtfData} mmtfData */ function decodeMmtf( inputDict, params ){ params = params || {}; var ignoreFields = params.ignoreFields; var outputDict = {}; AllFields.forEach( function( name ){ var ignore = ignoreFields ? ignoreFields.indexOf( name ) !== -1 : false; var data = inputDict[ name ]; if( !ignore && data !== undefined ){ if( data instanceof Uint8Array ){ outputDict[ name ] = performDecoding.apply( null, decodeBytes( data ) ); }else{ outputDict[ name ] = data; } } } ); return outputDict; } /** * @file mmtf-traverse * @private * @author Alexander Rose <alexander.rose@weirdbyte.de> */ /** * mmtf traverse module. * @module MmtfTraverse */ /** * Converts an array of ASCII codes trimming '\0' bytes * @private * @param {Array} charCodeArray - array of ASCII char codes * @return {String} '\0' trimmed string */ function fromCharCode( charCodeArray ){ return String.fromCharCode.apply( null, charCodeArray ).replace(/\0/g, ''); } /** * @callback module:MmtfTraverse.onModel * @param {Object} modelData * @param {Integer} modelData.chainCount - number of chains in the model * @param {Integer} modelData.modelIndex - index of the model */ /** * @callback module:MmtfTraverse.onChain * @param {Object} chainData * @param {Integer} chainData.groupCount - number of groups in the chain * @param {Integer} chainData.chainIndex - index of the chain * @param {Integer} chainData.modelIndex - index of the parent model * @param {String} chainData.chainId - chain id * @param {?String} chainData.chainName - additional chain name */ /** * @callback module:MmtfTraverse.onGroup * @param {Object} groupData * @param {Integer} groupData.atomCount - number of atoms in the group * @param {Integer} groupData.groupIndex - index of the group * @param {Integer} groupData.chainIndex - index of the parent chain * @param {Integer} groupData.modelIndex - index of the parent model * @param {Integer} groupData.groupId - group id (residue number) * @param {Integer} groupData.groupType - index to an entry in {@link module:MmtfDecode.MmtfData}#groupList * @param {String} groupData.groupName - name of the group, 0 to 5 characters * @param {Char} groupData.singleLetterCode - IUPAC single letter code, otherwise 'X', 1 character * @param {String} groupData.chemCompType - chemical component type from the mmCIF dictionary * @param {?Integer} groupData.secStruct - sencoded secondary structure | * 0: pi helix, 1: bend, 2: alpha helix, 3: extended, * 4: 3-10 helix, 5: bridge, 6: turn, 7: coil, -1: undefined * @param {?Char} groupData.insCode - insertion code * @param {?Integer} groupData.sequenceIndex - index to the `sequence` property of * the corresponding entity, -1 when the entity has no sequence */ /** * @callback module:MmtfTraverse.onAtom * @param {Object} atomData * @param {Integer} atomData.atomIndex - index of the atom * @param {Integer} atomData.groupIndex - index of the parent group * @param {Integer} atomData.chainIndex - index of the parent chain * @param {Integer} atomData.modelIndex - index of the parent model * @param {?Integer} atomData.atomId - atom id * @param {String} atomData.element - IUPAC element name, 0 to 3 characters * @param {String} atomData.atomName - name of the atom, 0 to 5 characters * @param {Integer} atomData.formalCharge - formal charge of the atom * @param {Float} atomData.xCoord - x coordinate in Å * @param {Float} atomData.yCoord - y coordinate in Å * @param {Float} atomData.zCoord - z coordinate in Å * @param {?Float} atomData.bFactor - B-factor in in Å^2 * @param {?Char} atomData.altLoc - alternate location identifier * @param {?Float} atomData.occupancy - occupancy of the atom */ /** * @callback module:MmtfTraverse.onBond * @param {Object} bondData * @param {Integer} bondData.atomIndex1 - index of the first atom * @param {Integer} bondData.atomIndex2 - index of the secound atom * @param {Integer} bondData.bondOrder - bond order, allowed values are 1 to 3 */ /** * Traverse the MMTF structure data. * @static * @param {module:MmtfDecode.MmtfData} mmtfData - decoded mmtf data * @param {Object} eventCallbacks * @param {module:MmtfTraverse.onModel} [eventCallbacks.onModel] - called for each model * @param {module:MmtfTraverse.onChain} [eventCallbacks.onChain] - called for each chain * @param {module:MmtfTraverse.onGroup} [eventCallbacks.onGroup] - called for each group * @param {module:MmtfTraverse.onAtom} [eventCallbacks.onAtom] - called for each atom * @param {module:MmtfTraverse.onBond} [eventCallbacks.onBond] - called for each bond * @param {Object} [params] - traversal parameters * @param {Boolean} [params.firstModelOnly] - traverse only the first model */ function traverseMmtf( mmtfData, eventCallbacks, params ){ params = params || {}; var firstModelOnly = params.firstModelOnly; // setup callbacks var onModel = eventCallbacks.onModel; var onChain = eventCallbacks.onChain; var onGroup = eventCallbacks.onGroup; var onAtom = eventCallbacks.onAtom; var onBond = eventCallbacks.onBond; // setup index counters var modelIndex = 0; var chainIndex = 0; var groupIndex = 0; var atomIndex = 0; var modelFirstAtomIndex = 0; var modelLastAtomIndex = -1; // setup optional fields