//import * as THREE from "../../libs/three.js/build/three.module.js"; import {Version} from "../Version.js"; import {PointAttributes, PointAttribute, PointAttributeTypes} from "../loader/PointAttributes.js"; const typedArrayMapping = { "int8": Int8Array, "int16": Int16Array, "int32": Int32Array, "int64": Float64Array, "uint8": Uint8Array, "uint16": Uint16Array, "uint32": Uint32Array, "uint64": Float64Array, "float": Float32Array, "double": Float64Array, }; const gs3dProplist = [ 'f_dc_0', //color 'f_dc_1', 'f_dc_2', 'f_rest_0', // has sh if not null at origin inriav1parser.js 'f_rest_1', 'f_rest_2', 'f_rest_3', 'f_rest_4', 'f_rest_5', 'f_rest_6', 'f_rest_7', 'f_rest_8', 'f_rest_9', 'f_rest_10', 'f_rest_11', 'f_rest_12', 'f_rest_13', 'f_rest_14', 'f_rest_15', 'f_rest_16', 'f_rest_17', 'f_rest_18', 'f_rest_19', 'f_rest_20', 'f_rest_21', 'f_rest_22', 'f_rest_23', 'f_rest_24', 'f_rest_25', 'f_rest_26', 'f_rest_27', 'f_rest_28', 'f_rest_29', 'f_rest_30', 'f_rest_31', 'f_rest_32', 'f_rest_33', 'f_rest_34', 'f_rest_35', 'f_rest_36', 'f_rest_37', 'f_rest_38', 'f_rest_39', 'f_rest_40', 'f_rest_41', 'f_rest_42', 'f_rest_43', 'f_rest_44', 'opacity', 'scale_0', 'scale_1', 'scale_2', 'rot_0', 'rot_1', 'rot_2', 'rot_3' ] let clamp = function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); } class Vector3 { constructor( x = 0, y = 0, z = 0 ) { Object.defineProperty( this, 'isVector3', { value: true } ); this.x = x; this.y = y; this.z = z; } set( x, y, z ) { if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } } class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { Object.defineProperty( this, 'isQuaternion', { value: true } ); this._x = x; this._y = y; this._z = z; this._w = w; } set( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; //this._onChangeCallback(); return this; } } const _zero = new Vector3( 0, 0, 0 ); const _one = new Vector3( 1, 1, 1 ); class Matrix4 { constructor() { Object.defineProperty( this, 'isMatrix4', { value: true } ); this.elements = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); } } makeRotationFromQuaternion( q ) { return this.compose( _zero, q, _one ); } compose( position, quaternion, scale ) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; te[ 1 ] = ( xy + wz ) * sx; te[ 2 ] = ( xz - wy ) * sx; te[ 3 ] = 0; te[ 4 ] = ( xy - wz ) * sy; te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; te[ 6 ] = ( yz + wx ) * sy; te[ 7 ] = 0; te[ 8 ] = ( xz + wy ) * sz; te[ 9 ] = ( yz - wx ) * sz; te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; te[ 11 ] = 0; te[ 12 ] = position.x; te[ 13 ] = position.y; te[ 14 ] = position.z; te[ 15 ] = 1; return this; } makeScale( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; } set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { const te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; } } class Matrix3 { constructor() { Object.defineProperty( this, 'isMatrix3', { value: true } ); this.elements = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; if ( arguments.length > 0 ) { console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); } } copy( m ) { const te = this.elements; const me = m.elements; te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; } multiply( m ) { return this.multiplyMatrices( this, m ); } premultiply( m ) { return this.multiplyMatrices( m, this ); } multiplyMatrices( a, b ) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; return this; } setFromMatrix4( m ) { const me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; } set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { const te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; } transpose() { let tmp; const m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; } } let toHalfFloat = function ( val ) { // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 /* This method is faster than the OpenEXR implementation (very often * used, eg. in Ogre), with the additional benefit of rounding, inspired * by James Tursa?s half-precision code. */ _floatView[ 0 ] = val; const x = _int32View[ 0 ]; let bits = ( x >> 16 ) & 0x8000; /* Get the sign */ let m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */ const e = ( x >> 23 ) & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if ( e < 103 ) return bits; /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if ( e > 142 ) { bits |= 0x7c00; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff ); return bits; } /* If exponent underflows but not too much, return a denorma l */ if ( e < 113 ) { m |= 0x0800; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 ); return bits; } bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 ); /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } let computeCovariance = function() {//通过旋转和缩放计算协方差(决定姿态)得到6个数 const tempMatrix4 = new Matrix4(); const scaleMatrix = new Matrix3(); const rotationMatrix = new Matrix3(); const covarianceMatrix = new Matrix3(); const transformedCovariance = new Matrix3(); const transform3x3 = new Matrix3(); const transform3x3Transpose = new Matrix3(); const thf = toHalfFloat //THREE.DataUtils.toHalfFloat.bind(THREE.DataUtils); return function(scale, rotation, transform, outCovariance, outOffset = 0, desiredOutputCompressionLevel) { tempMatrix4.makeScale(scale.x, scale.y, scale.z); scaleMatrix.setFromMatrix4(tempMatrix4); tempMatrix4.makeRotationFromQuaternion(rotation); rotationMatrix.setFromMatrix4(tempMatrix4); covarianceMatrix.copy(rotationMatrix).multiply(scaleMatrix); transformedCovariance.copy(covarianceMatrix).transpose().premultiply(covarianceMatrix); //为什么要乘以自己的转置呀? if (transform) {//场景的整体transform 如果可能移动就不传,在sort时直接乘 transform3x3.setFromMatrix4(transform); transform3x3Transpose.copy(transform3x3).transpose(); transformedCovariance.multiply(transform3x3Transpose); transformedCovariance.premultiply(transform3x3); } if (desiredOutputCompressionLevel === 1) {//压缩 outCovariance[outOffset] = thf(transformedCovariance.elements[0]); outCovariance[outOffset + 1] = thf(transformedCovariance.elements[3]); outCovariance[outOffset + 2] = thf(transformedCovariance.elements[6]); outCovariance[outOffset + 3] = thf(transformedCovariance.elements[4]); outCovariance[outOffset + 4] = thf(transformedCovariance.elements[7]); outCovariance[outOffset + 5] = thf(transformedCovariance.elements[8]); } else { outCovariance[outOffset] = transformedCovariance.elements[0]; outCovariance[outOffset + 1] = transformedCovariance.elements[3]; outCovariance[outOffset + 2] = transformedCovariance.elements[6]; outCovariance[outOffset + 3] = transformedCovariance.elements[4]; outCovariance[outOffset + 4] = transformedCovariance.elements[7]; outCovariance[outOffset + 5] = transformedCovariance.elements[8]; } /* Vrk 中的元素分别是transformedCovariance的 [ 0,3,6, 3,4,7, 6,7,8 ] */ }; }(); Potree = {}; onmessage = function (event) { performance.mark("binary-decoder-start"); let buffer = event.data.buffer; let pointAttributes = event.data.pointAttributes; let numPoints = buffer.byteLength / pointAttributes.byteSize; let view = new DataView(buffer); let version = new Version(event.data.version); let nodeOffset = event.data.offset; let scale = event.data.scale; let spacing = event.data.spacing; let hasChildren = event.data.hasChildren; let name = event.data.name; let tightBoxMin = [ Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY ]; let tightBoxMax = [ Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY ]; let mean = [0, 0, 0]; let pos = event.data.min // add geometryNode.sceneNode.position node的偏移 let attributeBuffers = {}; let inOffset = 0; let hasGS3D = pointAttributes.attributes.some(e=>e.name == 'GS3D') for (let pointAttribute of pointAttributes.attributes) { if (pointAttribute.name === "POSITION_CARTESIAN") { let buff = new ArrayBuffer(numPoints * 4 * 3); let positions = new Float32Array(buff); for (let j = 0; j < numPoints; j++) { let x, y, z; if (version.newerThan('1.3')) { x = (view.getUint32(inOffset + j * pointAttributes.byteSize + 0, true) * scale); y = (view.getUint32(inOffset + j * pointAttributes.byteSize + 4, true) * scale); z = (view.getUint32(inOffset + j * pointAttributes.byteSize + 8, true) * scale); } else { x = view.getFloat32(j * pointAttributes.byteSize + 0, true) + nodeOffset[0]; y = view.getFloat32(j * pointAttributes.byteSize + 4, true) + nodeOffset[1]; z = view.getFloat32(j * pointAttributes.byteSize + 8, true) + nodeOffset[2]; } positions[3 * j + 0] = x; positions[3 * j + 1] = y; positions[3 * j + 2] = z; mean[0] += x / numPoints; mean[1] += y / numPoints; mean[2] += z / numPoints; tightBoxMin[0] = Math.min(tightBoxMin[0], x); tightBoxMin[1] = Math.min(tightBoxMin[1], y); tightBoxMin[2] = Math.min(tightBoxMin[2], z); tightBoxMax[0] = Math.max(tightBoxMax[0], x); tightBoxMax[1] = Math.max(tightBoxMax[1], y); tightBoxMax[2] = Math.max(tightBoxMax[2], z); } attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; if(hasGS3D){//add let buff2 = new ArrayBuffer(numPoints * 4 * 4); let buff3 = new ArrayBuffer(numPoints * 4 * 3); let centersInt = new Int32Array(buff2); let centersFloat = new Float32Array(buff3); for(let i=0;i= 0) { x = u; y = v; } else { x = -(v / Math.sign(v) - 1) / Math.sign(u); y = -(u / Math.sign(u) - 1) / Math.sign(v); } let length = Math.sqrt(x * x + y * y + z * z); //因法线长度固定为1,所以只需要xy就能算出z(不过这里似乎是约定相加为1?) x = x / length; //x和y都只有一个byte,所以精度很低,只有256个分段 y = y / length; z = z / length; normals[3 * j + 0] = x; normals[3 * j + 1] = y; normals[3 * j + 2] = z; } attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; } else if (pointAttribute.name === "NORMAL") { let buff = new ArrayBuffer(numPoints * 4 * 3); let normals = new Float32Array(buff); for (let j = 0; j < numPoints; j++) { let x = view.getFloat32(inOffset + j * pointAttributes.byteSize + 0, true); let y = view.getFloat32(inOffset + j * pointAttributes.byteSize + 4, true); let z = view.getFloat32(inOffset + j * pointAttributes.byteSize + 8, true); normals[3 * j + 0] = x; normals[3 * j + 1] = y; normals[3 * j + 2] = z; } attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; }else if (pointAttribute.name === "IR" || pointAttribute.name === "TEMP" ) { //温度 ir是热成像的温度 temp是AI识别的温度 let buff = new ArrayBuffer(numPoints * 4); //原本数据是uint16但是shader没办法接收,只能转了 let f32 = new Float32Array(buff); //according to pointAttribute.type.name, 取值范围为 0 到 65535。 let min = Infinity, max = -Infinity, v for (let j = 0; j < numPoints; j++) {//仿照position的写法 填入数据 f32[j] = v = view.getUint16(inOffset + j * pointAttributes.byteSize, true) / 10 //开尔文 v != 0 && (min = Math.min(min, v)) //==0可能是没探测到所以跳过 v != 0 && (max = Math.max(max, v)) }//ir or temp 里面的值除以10就是开尔文温度 attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; pointAttribute.range = [min, max]; //但每个geo范围不一样 }else if (pointAttribute.name === "SEG"){ //类型 /* 1: concrete 2: wood 3: metal 4: glass 5: plastic 6: electric_wire */ let buff = new ArrayBuffer(numPoints * 4); let f32 = new Float32Array(buff); //according to pointAttribute.type.name, 取值范围为 0 到 65535。 for (let j = 0; j < numPoints; j++) {//仿照position的写法 填入数据 f32[j] = view.getUint16(inOffset + j * pointAttributes.byteSize, true) }//ir or temp 里面的值除以10就是开尔文温度哈 attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; } else if (pointAttribute.name === "GS3D") {//add 见inriav1plyparser.js ////////////////////////////////////////////////////////// let buff = new ArrayBuffer(numPoints * pointAttribute.byteSize); let f32 = new Float32Array(buff); for (let j = 0; j < numPoints; j++) {//仿照position的写法 填入数据 for (let i = 0; i < pointAttribute.numElements; i++) { //f32[pointAttribute.numElements * j + i] = view.getUint32( inOffset + j * pointAttributes.byteSize + 4*i, true) * scale ; f32[pointAttribute.numElements * j + i] = view.getFloat32(inOffset + j * pointAttributes.byteSize + 4*i, true) //+ nodeOffset[0]; //是Uint32还是Float32? PlyParserUtils.js中写的是getFloat32 ( rawVertex[fieldId] = vertexData.getFloat32(offset + fieldOffsets[fieldId], true);) } } attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute }; //得到颜色 let buff2 = new ArrayBuffer(numPoints * 4); let colors = new Uint8Array(buff2); const SH_C0 = 0.28209479177387814; const offset_opa = gs3dProplist.indexOf('opacity') const offset_col = gs3dProplist.indexOf('f_dc_0') let getColor = (index)=>{ let value = (0.5 + SH_C0 * f32[index+offset_col]) * 255; return clamp(Math.floor(value), 0, 255); } let getOpacity = (index)=>{ let value = (1 / (1 + Math.exp(-f32[index+offset_opa]))) * 255; return clamp(Math.floor(value), 0, 255); } for (let j = 0; j < numPoints; j++) { colors[4 * j + 0] = getColor(j * pointAttribute.numElements + 0) colors[4 * j + 1] = getColor(j * pointAttribute.numElements + 1) colors[4 * j + 2] = getColor(j * pointAttribute.numElements + 2) colors[4 * j + 3] = getOpacity(j * pointAttribute.numElements) } attributeBuffers['rgba'] = { buffer: buff2, attribute: pointAttribute }; //compute cov: let buff3 = new ArrayBuffer(numPoints * 24); let covs = new Float32Array(buff3); const offset_scale = gs3dProplist.indexOf('scale_0') //第49个数开始是 const offset_rot = gs3dProplist.indexOf('rot_0') let scale = new Vector3() let quaternion = new Quaternion() let getScale = (index)=>{ let get = (offset)=>{ return Math.exp(f32[index * pointAttribute.numElements + offset + offset_scale]); } scale.set(get(0), get(1), get(2)) } let getQuatenion = (index)=>{ let get = (offset)=>{ return f32[index * pointAttribute.numElements + offset + offset_rot] } //quaternion.set( get(0), get(1), get(2), get(3)) quaternion.set(get(1), get(2), get(3), get(0)) //w放到最后 另外如果compressionLevel不是0的话还要再加一步,见this.fbf(sectionFloatArray[rotationBase + 1] //quaternion.normalize(); } for (let j = 0; j < numPoints; j++) { getScale(j); getQuatenion(j); computeCovariance(scale, quaternion, null, covs, 6 * j ); } attributeBuffers['covs'] = { buffer: buff3, attribute: pointAttribute }; //sh if(0){ let sh = Potree.settings.splatSH let maxRest = 3 * ((sh+1)*(sh+1) - 1) //sh=2时要24个数 let buff = new ArrayBuffer(numPoints * maxRest*4); //let shArray = new Float32Array(buff); let subArray const offset = gs3dProplist.indexOf('f_rest_0') for (let j = 0; j < numPoints; j++) { //let destView = shArray.subarray(j*maxRest, maxRest); let byteOffset = 4*j*maxRest let destView = new Float32Array(buff, byteOffset , maxRest); let start = j * pointAttribute.numElements+offset subArray = f32.subarray(start, start+maxRest); destView.set(subArray) } attributeBuffers['sh'] = { buffer: buff, attribute: pointAttribute }; } //////////////////////////////////////////////////////////////////////////////// }else{ let buff = new ArrayBuffer(numPoints * 4); let f32 = new Float32Array(buff); let TypedArray = typedArrayMapping[pointAttribute.type.name]; preciseBuffer = new TypedArray(numPoints); let [min, max] = [Infinity, -Infinity]; let [offset, scale] = [0, 1]; const getterMap = { "int8": view.getInt8, "int16": view.getInt16, "int32": view.getInt32, "int64": view.getInt64, "uint8": view.getUint8, "uint16": view.getUint16, "uint32": view.getUint32, "uint64": view.getUint64, "float": view.getFloat32, "double": view.getFloat64, }; const getter = getterMap[pointAttribute.type.name].bind(view); // compute offset and scale to pack larger types into 32 bit floats if(pointAttribute.type.size > 4){ for(let j = 0; j < numPoints; j++){ let value = getter(inOffset + j * pointAttributes.byteSize, true); if(!Number.isNaN(value)){ min = Math.min(min, value); max = Math.max(max, value); } } if(pointAttribute.initialRange != null){ offset = pointAttribute.initialRange[0]; scale = 1 / (pointAttribute.initialRange[1] - pointAttribute.initialRange[0]); }else{ offset = min; scale = 1 / (max - min); } } for(let j = 0; j < numPoints; j++){ let value = getter(inOffset + j * pointAttributes.byteSize, true); if(!Number.isNaN(value)){ min = Math.min(min, value); max = Math.max(max, value); } f32[j] = (value - offset) * scale; preciseBuffer[j] = value; } pointAttribute.range = [min, max]; attributeBuffers[pointAttribute.name] = { buffer: buff, preciseBuffer: preciseBuffer, attribute: pointAttribute, offset: offset, scale: scale, }; } inOffset += pointAttribute.byteSize; } { // add indices let buff = new ArrayBuffer(numPoints * 4); let indices = new Uint32Array(buff); for (let i = 0; i < numPoints; i++) { indices[i] = i; } attributeBuffers["INDICES"] = { buffer: buff, attribute: PointAttribute.INDICES }; } { // handle attribute vectors let vectors = pointAttributes.vectors; for(let vector of vectors){ let {name, attributes} = vector; let numVectorElements = attributes.length; let buffer = new ArrayBuffer(numVectorElements * numPoints * 4); let f32 = new Float32Array(buffer); let iElement = 0; for(let sourceName of attributes){ let sourceBuffer = attributeBuffers[sourceName]; let {offset, scale} = sourceBuffer; let view = new DataView(sourceBuffer.buffer); const getter = view.getFloat32.bind(view); for(let j = 0; j < numPoints; j++){ let value = getter(j * 4, true); f32[j * numVectorElements + iElement] = (value / scale) + offset; } iElement++; } let vecAttribute = new PointAttribute(name, PointAttributeTypes.DATA_TYPE_FLOAT, 3); attributeBuffers[name] = { buffer: buffer, attribute: vecAttribute, }; } } performance.mark("binary-decoder-end"); // { // print timings // //performance.measure("spacing", "spacing-start", "spacing-end"); // performance.measure("binary-decoder", "binary-decoder-start", "binary-decoder-end"); // let measure = performance.getEntriesByType("measure")[0]; // let dpp = 1000 * measure.duration / numPoints; // let pps = parseInt(numPoints / (measure.duration / 1000)); // let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${pps.toLocaleString()} points/sec`; // console.log(debugMessage); // } performance.clearMarks(); performance.clearMeasures(); let message = { buffer: buffer, mean: mean, attributeBuffers: attributeBuffers, tightBoundingBox: { min: tightBoxMin, max: tightBoxMax }, }; let transferables = []; for (let property in message.attributeBuffers) { transferables.push(message.attributeBuffers[property].buffer); } transferables.push(buffer); postMessage(message, transferables); };