gazeplotter
Version:
Gazeplotter is a Svelte application for visualizing eye-tracking data.
96 lines (95 loc) • 3.08 kB
JavaScript
import { EyePipeline } from '../class/Eye/EyePipeline/EyePipeline.ts';
/**
* A worker file handling whole eyefiles processing.
* It is a separate file to avoid blocking the main thread.
*/
let fileNames = [];
let pipeline = null;
let streams = [];
let userInputResolver;
const isStringArray = (data) => {
if (!Array.isArray(data))
return false;
if (data.length === 0)
return false;
return typeof data[0] === 'string';
};
const isReadableStream = (data) => {
if (typeof data !== 'object')
return false;
if (data === null)
return false;
return typeof data.getReader === 'function';
};
const requestUserInput = () => {
self.postMessage({ type: 'request-user-input' });
return new Promise(resolve => {
userInputResolver = resolve;
});
};
self.onmessage = async (e) => await processEvent(e);
async function processEvent(e) {
const { data, type } = e.data;
switch (type) {
case 'file-names':
if (!isStringArray(data))
throw new Error('File names are not string[]');
fileNames = data;
pipeline = new EyePipeline(fileNames, requestUserInput);
return;
case 'test-stream':
if (!isReadableStream(data))
throw new Error('Stream is not ReadableStream');
return;
case 'stream':
return await evalStream(data);
case 'buffer':
return await evalBuffer(data);
case 'user-input':
return userInputResolver(data);
default:
throw new Error('Unknown const type in worker', data);
}
}
/**
* Converts ArrayBuffer to ReadableStream and passes it to evalStream
* @param buffer - The buffer to convert.
*/
const evalBuffer = async (buffer) => {
const chunkSize = 1024 * 1024;
const chunks = Math.ceil(buffer.byteLength / chunkSize);
const stream = new ReadableStream({
start(controller) {
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, buffer.byteLength);
controller.enqueue(new Uint8Array(buffer.slice(start, end)));
}
controller.close();
},
});
await evalStream(stream);
};
/**
* Adds a new stream to the pipeline and processes it if all streams are present.
* @param rs - The stream to add.
*/
const evalStream = async (rs) => {
if (pipeline === null)
throw new Error('Pipeline is not initialized');
if (fileNames.length === 0)
throw new Error('No files to process');
streams.push(rs);
// if have everything, process
if (streams.length === fileNames.length) {
for (const stream of streams) {
const data = await pipeline.addNewStream(stream);
if (data !== null) {
console.log('Done', data);
self.postMessage({ type: 'done', data });
streams = [];
fileNames = [];
}
}
}
};