UNPKG

js-subpcd

Version:

🌟 High-performance JavaScript/TypeScript QuadTree point cloud filtering and processing library. Published on npm as js-subpcd with PCL.js compatible API for spatial filtering, subsampling, and nearest neighbor search.

218 lines (217 loc) • 6.96 kB
"use strict"; /** * QuadTree Worker for Node.js using worker_threads * Handles heavy QuadTree operations in a separate thread */ Object.defineProperty(exports, "__esModule", { value: true }); const worker_threads_1 = require("worker_threads"); const index_1 = require("./index"); // Worker state let quadTreeFilter = null; let isBuilt = false; let isAborted = false; /** * Handle incoming messages from main thread */ function handleMessage(message) { if (!worker_threads_1.parentPort) return; try { switch (message.type) { case 'BUILD_TREE': handleBuildTree(message.data); break; case 'FILTER_BY_DEPTH': handleFilterByDepth(message.data); break; case 'FILTER_BY_GRID_SPACING': handleFilterByGridSpacing(message.data); break; case 'ABORT': handleAbort(); break; default: throw new Error(`Unknown message type: ${message.type}`); } } catch (error) { const response = { type: 'ERROR', error: error.message || 'Unknown worker error' }; worker_threads_1.parentPort.postMessage(response); } } /** * Handle tree building */ function handleBuildTree(data) { if (!worker_threads_1.parentPort) return; try { console.log(`Worker: Building QuadTree for ${data.points.length} points`); const startTime = performance.now(); // Reset abort flag isAborted = false; // Create QuadTree filter quadTreeFilter = new index_1.QuadTreeFilter(data.minCellSize); // Convert points to PointXYZ objects const points = data.points.map(p => new index_1.PointXYZ(p.x, p.y, p.z)); // Set points directly quadTreeFilter.dataLoader.points = points; // Build the tree const coords = quadTreeFilter.dataLoader.getCoordinateArrays(); quadTreeFilter.quadtreeManager.buildTreeFromArrays(coords.x, coords.y, coords.z, data.bounds); const buildTime = performance.now() - startTime; isBuilt = true; if (!isAborted) { const response = { type: 'TREE_BUILT', data: { pointCount: data.points.length, bounds: data.bounds, buildTime: buildTime }, processingTime: buildTime, pointCount: data.points.length, bounds: data.bounds }; worker_threads_1.parentPort.postMessage(response); } } catch (error) { const response = { type: 'ERROR', error: `Failed to build tree: ${error.message}` }; worker_threads_1.parentPort.postMessage(response); } } /** * Handle filter by depth */ async function handleFilterByDepth(data) { if (!worker_threads_1.parentPort) return; try { if (!quadTreeFilter || !isBuilt) { throw new Error('QuadTree not built'); } console.log(`Worker: Filtering by depth ${data.targetDepth}`); const startTime = performance.now(); // Reset abort flag isAborted = false; // Perform filtering const filteredPoints = await quadTreeFilter.filterByDepth(data.targetDepth); const filterTime = performance.now() - startTime; if (!isAborted) { const gridSize = Math.pow(2, data.targetDepth); const response = { type: 'POINTS_FILTERED', points: filteredPoints, processingTime: filterTime, depth: data.targetDepth, gridSize: gridSize, expectedMax: gridSize * gridSize, message: `Filtered to ${filteredPoints.length} points using ${gridSize}x${gridSize} grid` }; worker_threads_1.parentPort.postMessage(response); } } catch (error) { const response = { type: 'ERROR', error: `Failed to filter by depth: ${error.message}` }; worker_threads_1.parentPort.postMessage(response); } } /** * Handle filter by grid spacing */ async function handleFilterByGridSpacing(data) { if (!worker_threads_1.parentPort) return; try { if (!quadTreeFilter) { throw new Error('QuadTree filter not initialized'); } console.log(`Worker: Filtering by grid spacing ${data.minLateralSpacing}`); const startTime = performance.now(); // Reset abort flag isAborted = false; // Perform filtering const filteredPoints = quadTreeFilter.filterPointsByGridSpacing(data.xCoords, data.yCoords, data.zCoords, data.minLateralSpacing); const filterTime = performance.now() - startTime; if (!isAborted) { const response = { type: 'POINTS_FILTERED', points: filteredPoints, processingTime: filterTime, message: `Filtered to ${filteredPoints.length} points with ${data.minLateralSpacing}m spacing` }; worker_threads_1.parentPort.postMessage(response); } } catch (error) { const response = { type: 'ERROR', error: `Failed to filter by grid spacing: ${error.message}` }; worker_threads_1.parentPort.postMessage(response); } } /** * Handle abort signal */ function handleAbort() { console.log('Worker: Received abort signal'); isAborted = true; quadTreeFilter = null; isBuilt = false; } /** * Send progress updates */ function sendProgress(progress, message) { if (!worker_threads_1.parentPort || isAborted) return; const response = { type: 'PROGRESS', progress: progress, message: message }; worker_threads_1.parentPort.postMessage(response); } // Set up message handler if (worker_threads_1.parentPort) { worker_threads_1.parentPort.on('message', handleMessage); // Send ready signal worker_threads_1.parentPort.postMessage({ type: 'READY', message: 'QuadTree worker ready' }); } else { console.error('Worker: parentPort not available'); } // Handle uncaught exceptions process.on('uncaughtException', (error) => { if (worker_threads_1.parentPort) { worker_threads_1.parentPort.postMessage({ type: 'ERROR', error: `Uncaught exception: ${error.message}` }); } process.exit(1); }); // Handle unhandled rejections process.on('unhandledRejection', (reason) => { if (worker_threads_1.parentPort) { worker_threads_1.parentPort.postMessage({ type: 'ERROR', error: `Unhandled rejection: ${reason}` }); } process.exit(1); });