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
JavaScript
;
/**
* 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);
});