Created
August 6, 2025 00:41
-
-
Save lardratboy/d0b7e2997744aaa4744b003aa8d63589 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Data type configuration constants | |
| const DATA_TYPES = { | |
| int8: { size: 1, min: -128, max: 127, method: 'getInt8' }, | |
| uint8: { size: 1, min: 0, max: 255, method: 'getUint8' }, | |
| int16: { size: 2, min: -32768, max: 32767, method: 'getInt16' }, | |
| uint16: { size: 2, min: 0, max: 65535, method: 'getUint16' }, | |
| int32: { size: 4, min: -2147483648, max: 2147483647, method: 'getInt32' }, | |
| uint32: { size: 4, min: 0, max: 4294967295, method: 'getUint32' } | |
| }; | |
| // Pre-calculate normalization multipliers for each data type | |
| const NORMALIZERS = Object.fromEntries( | |
| Object.entries(DATA_TYPES).map(([type, config]) => [ | |
| type, | |
| { | |
| multiplier: 2 / (config.max - config.min), | |
| offset: config.min | |
| } | |
| ]) | |
| ); | |
| /** | |
| * Process binary data into normalized 3D points with colors using configurable quantization | |
| * @param {ArrayBuffer} buffer - The input binary data | |
| * @param {string} dataType - The data type to interpret the buffer as | |
| * @param {boolean} isLittleEndian - Whether to read as little endian | |
| * @param {number} quantizationBits - Number of bits for quantization (2-10) | |
| * @returns {{ points: Float32Array, colors: Float32Array, numPoints: number }} | |
| */ | |
| function quantizeProcessDataAs(buffer, dataType, isLittleEndian, quantizationBits = 10) { | |
| // Input validation | |
| if (!buffer || !(buffer instanceof ArrayBuffer)) { | |
| throw new Error('Invalid buffer provided - must be an ArrayBuffer'); | |
| } | |
| const config = DATA_TYPES[dataType]; | |
| if (!config) { | |
| throw new Error(`Unsupported data type: ${dataType}. Supported types: ${Object.keys(DATA_TYPES).join(', ')}`); | |
| } | |
| // Validate quantization bits | |
| if (!Number.isInteger(quantizationBits) || quantizationBits < 2 || quantizationBits > 10) { | |
| throw new Error('quantizationBits must be an integer between 2 and 10'); | |
| } | |
| // Check if total bits would exceed safe integer limits | |
| const totalBits = quantizationBits * 3; | |
| if (totalBits > 30) { | |
| throw new Error(`Total quantization bits (${totalBits}) would exceed safe integer limits. Max 30 bits (10 bits per dimension)`); | |
| } | |
| const typeSize = config.size; | |
| const tupleSize = typeSize * 3; | |
| if (buffer.byteLength < tupleSize) { | |
| throw new Error(`Buffer too small for data type ${dataType}. Need at least ${tupleSize} bytes, got ${buffer.byteLength}`); | |
| } | |
| const view = new DataView(buffer); | |
| const maxOffset = buffer.byteLength - tupleSize; | |
| const maxTuples = Math.floor(buffer.byteLength / tupleSize); | |
| // Pre-allocate typed arrays for better performance | |
| const points = new Float32Array(maxTuples * 3); | |
| const colors = new Float32Array(maxTuples * 3); | |
| // Cache normalization values and methods | |
| const { multiplier, offset } = NORMALIZERS[dataType]; | |
| const readMethod = view[config.method].bind(view); | |
| const normalize = value => ((value - offset) * multiplier) - 1; | |
| let pointIndex = 0; | |
| let baseOffset = 0; | |
| // Calculate quantization parameters based on bit count | |
| const qRange = 1 << quantizationBits; // 2^quantizationBits | |
| const qHalfRange = qRange / 2; | |
| const qMaxIndex = qRange - 1; | |
| // Bit array sized for qRange^3 possible quantized positions | |
| const totalQuantizedPositions = qRange * qRange * qRange; | |
| const bitArraySizeInUint32 = Math.ceil(totalQuantizedPositions / 32); | |
| const tupleBitArray = new Uint32Array(bitArraySizeInUint32); | |
| // Calculate bit shift amounts for index creation | |
| const yShift = quantizationBits; | |
| const zShift = quantizationBits * 2; | |
| try { | |
| while (baseOffset <= maxOffset) { | |
| // Read and normalize all three coordinates | |
| const x = normalize(readMethod(baseOffset, isLittleEndian)); | |
| const y = normalize(readMethod(baseOffset + typeSize, isLittleEndian)); | |
| const z = normalize(readMethod(baseOffset + typeSize * 2, isLittleEndian)); | |
| // Quantize coordinates: map [-1,1] to [0, qMaxIndex] with bounds checking | |
| const qx = Math.max(0, Math.min(qMaxIndex, Math.floor((x + 1) * qHalfRange))); | |
| const qy = Math.max(0, Math.min(qMaxIndex, Math.floor((y + 1) * qHalfRange))); | |
| const qz = Math.max(0, Math.min(qMaxIndex, Math.floor((z + 1) * qHalfRange))); | |
| // Create unique index for this quantized position using variable bit shifts | |
| const qIndex = (qz << zShift) | (qy << yShift) | qx; | |
| // Check if we've seen this quantized position before | |
| const elementIndex = qIndex >>> 5; // Use unsigned right shift for bit array indexing | |
| const bitPosition = qIndex & 0x1F; | |
| const mask = 1 << bitPosition; | |
| if ((tupleBitArray[elementIndex] & mask) === 0) { | |
| // Mark this position as seen | |
| tupleBitArray[elementIndex] |= mask; | |
| // Store points (original normalized coordinates, not quantized) | |
| points[pointIndex] = x; | |
| points[pointIndex + 1] = y; | |
| points[pointIndex + 2] = z; | |
| // Store colors (mapped from [-1,1] to [0,1] for Three.js) | |
| colors[pointIndex] = (x + 1) / 2; | |
| colors[pointIndex + 1] = (y + 1) / 2; | |
| colors[pointIndex + 2] = (z + 1) / 2; | |
| pointIndex += 3; | |
| } | |
| baseOffset += tupleSize; | |
| } | |
| } catch (e) { | |
| console.error(`Error processing data at offset: ${baseOffset}`, e); | |
| // Return what we've processed so far rather than failing completely | |
| } | |
| // Trim arrays to actual size used | |
| const actualPoints = new Float32Array(points.buffer, 0, pointIndex); | |
| const actualColors = new Float32Array(colors.buffer, 0, pointIndex); | |
| console.log(`Quantization completed: ${quantizationBits} bits, ${qRange}^3 possible positions, ${pointIndex / 3} unique points found`); | |
| return { | |
| points: actualPoints, | |
| colors: actualColors, | |
| numPoints: pointIndex / 3, | |
| quantizationBits: quantizationBits, | |
| quantizationRange: qRange | |
| }; | |
| } | |
| export { quantizeProcessDataAs, DATA_TYPES }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment