import pako from 'pako';
export const decompressData = (compressedData) => {
try {
const decompressed = pako.inflate(compressedData);
const textDecoder = new TextDecoder('utf-8');
const jsonStr = textDecoder.decode(decompressed);
return JSON.parse(jsonStr);
} catch (error) {
console.error('Error decompressing data:', error);
return null;
}
};
export const sampleData = (data, threshold) => {
if (!Array.isArray(data) || data.length <= threshold) return data;
if (typeof data[0] === 'number') {
return lttbDownsample(data, threshold);
}
const samplingRate = Math.ceil(data.length / threshold);
return data.filter((_, index) => index % samplingRate === 0);
};
function lttbDownsample(data, threshold) {
if (data.length <= threshold) return data;
const sampled = new Array(threshold);
let sampledIndex = 0;
sampled[sampledIndex++] = data[0];
const bucketSize = (data.length - 2) / (threshold - 2);
let lastSelectedX = 0;
let lastSelectedY = data[0];
for (let i = 0; i < threshold - 2; i++) {
const bucketStart = Math.floor((i + 0) * bucketSize) + 1;
const bucketEnd = Math.floor((i + 1) * bucketSize) + 1;
const avgX = (bucketStart + bucketEnd) / 2;
let maxArea = -1;
let maxAreaPoint = data[bucketStart];
for (let j = bucketStart; j < bucketEnd; j++) {
const area =
Math.abs(
(lastSelectedX - avgX) * (data[j] - lastSelectedY) -
(lastSelectedX - j) * (avgX - lastSelectedY)
) * 0.5;
if (area > maxArea) {
maxArea = area;
maxAreaPoint = data[j];
}
}
sampled[sampledIndex++] = maxAreaPoint;
lastSelectedX = bucketEnd;
lastSelectedY = maxAreaPoint;
}
sampled[sampledIndex] = data[data.length - 1];
return sampled;
}
export const processDataInChunks = (data, chunkSize, callback) => {
if (!Array.isArray(data)) return callback(data);
const optimalChunkSize = Math.min(chunkSize, Math.max(100, Math.floor(data.length / 10)));
let currentIndex = 0;
let isProcessing = false;
const processNextChunk = () => {
if (isProcessing || currentIndex >= data.length) return;
isProcessing = true;
const chunk = data.slice(currentIndex, currentIndex + optimalChunkSize);
requestAnimationFrame(() => {
callback(chunk, currentIndex, data.length);
currentIndex += optimalChunkSize;
isProcessing = false;
if (currentIndex < data.length) {
processNextChunk();
}
});
};
processNextChunk();
};
export const debounce = (func, wait, immediate = false) => {
let timeout;
return function executedFunction(...args) {
const context = this;
const later = () => {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};