'use strict'; // Min/max (peak-envelope) decimation — O(n). Runs off-main-thread to avoid // blocking the render loop. // // The range is split into threshold/2 equal buckets and each contributes its // smallest and largest sample, in the order the two occurred — the way an // oscilloscope draws a trace it cannot show pixel-for-pixel. // // This replaced LTTB, which picks the sample forming the largest triangle with // its neighbours: a plausible-looking shape, but it silently drops a one-sample // spike whenever a smoother neighbour scores higher — exactly the sample worth // looking at. The envelope cannot drop it, because a spike is by definition its // bucket's min or max. Every output point is a real sample at its real // timestamp; nothing is interpolated or averaged. // // Kept identical to minMaxDecimate() in Common/Client/go/wshub/hub.go and to // decimate() in app.js, so a trace looks the same whichever thinned it. function decimate(t, v, threshold) { const len = t.length; if (len <= threshold || threshold < 4) { // Copy to new arrays so we can transfer them back without detaching the input. return { t: new Float64Array(t), v: new Float64Array(v) }; } const buckets = threshold >> 1; const outT = new Float64Array(threshold); const outV = new Float64Array(threshold); let n = 0; for (let b = 0; b < buckets; b++) { const lo = Math.floor(b * len / buckets); const hi = (b === buckets - 1) ? len : Math.floor((b + 1) * len / buckets); if (lo >= hi) continue; let iMin = lo, iMax = lo; for (let j = lo + 1; j < hi; j++) { if (v[j] < v[iMin]) iMin = j; if (v[j] > v[iMax]) iMax = j; } // Emit in time order so the result plots as one ascending trace. if (iMin > iMax) { const s = iMin; iMin = iMax; iMax = s; } outT[n] = t[iMin]; outV[n] = v[iMin]; n++; // A bucket whose samples are all equal has one extreme, not two. if (iMax !== iMin) { outT[n] = t[iMax]; outV[n] = v[iMax]; n++; } } // slice() so the transferred buffers are exactly the used length. return { t: outT.slice(0, n), v: outV.slice(0, n) }; } self.onmessage = function({ data: { id, t, v, threshold } }) { const result = decimate(t, v, threshold); // Transfer the output buffers back to the main thread zero-copy. self.postMessage({ id, t: result.t, v: result.v }, [result.t.buffer, result.v.buffer]); };