| 128 | } |
| 129 | |
| 130 | async function main(): Promise<void> { |
| 131 | const container = document.getElementById('chart'); |
| 132 | if (!(container instanceof HTMLElement)) { |
| 133 | throw new Error('Chart container not found'); |
| 134 | } |
| 135 | |
| 136 | // Create chart with initial empty data |
| 137 | const options: ChartGPUOptions = { |
| 138 | grid: { left: 70, right: 24, top: 24, bottom: 56 }, |
| 139 | xAxis: { type: 'value', name: 'Time' }, |
| 140 | yAxis: { type: 'value', min: -1.2, max: 1.2, name: 'Value' }, |
| 141 | palette: ['#4a9eff'], |
| 142 | animation: false, // Disable animation for streaming |
| 143 | series: [ |
| 144 | { |
| 145 | type: 'line', |
| 146 | name: 'stream', |
| 147 | data: [], |
| 148 | color: '#4a9eff', |
| 149 | lineStyle: { width: 1.5, opacity: 0.8 }, |
| 150 | sampling: 'lttb', |
| 151 | samplingThreshold: 5000, |
| 152 | }, |
| 153 | ], |
| 154 | }; |
| 155 | |
| 156 | // Create chart in worker for zero-copy Float32Array transfer support |
| 157 | const chart = await ChartGPU.createInWorker(container, options); |
| 158 | |
| 159 | // Resize handling |
| 160 | let resizeScheduled = false; |
| 161 | const ro = new ResizeObserver(() => { |
| 162 | if (resizeScheduled) return; |
| 163 | resizeScheduled = true; |
| 164 | requestAnimationFrame(() => { |
| 165 | resizeScheduled = false; |
| 166 | chart.resize(); |
| 167 | }); |
| 168 | }); |
| 169 | ro.observe(container); |
| 170 | chart.resize(); |
| 171 | |
| 172 | // Streaming state |
| 173 | let nextX = 0; |
| 174 | let totalPoints = 0; |
| 175 | let streaming = true; |
| 176 | |
| 177 | // Trackers |
| 178 | const fpsTracker = new FPSTracker(); |
| 179 | const dataRateTracker = new DataRateTracker(); |
| 180 | |
| 181 | // Streaming loop |
| 182 | let lastFrameTime = performance.now(); |
| 183 | let intervalId: number | null = null; |
| 184 | |
| 185 | const streamFrame = (): void => { |
| 186 | if (!streaming || totalPoints >= MAX_POINTS) { |
| 187 | if (intervalId !== null) { |