( totalPoints: number, zoomRange: ZoomRange | null, sampling: SeriesSampling, samplingThreshold: number )
| 139 | * Tradeoff: lower threshold = faster but less detail; 'none' = slower but exact. |
| 140 | */ |
| 141 | const estimateRenderedPoints = ( |
| 142 | totalPoints: number, |
| 143 | zoomRange: ZoomRange | null, |
| 144 | sampling: SeriesSampling, |
| 145 | samplingThreshold: number |
| 146 | ): number => { |
| 147 | const start = zoomRange?.start ?? 0; |
| 148 | const end = zoomRange?.end ?? 100; |
| 149 | const spanFrac = Math.max(0, Math.min(1, (end - start) / 100)); |
| 150 | |
| 151 | // With x=index and explicit x-domain [0..X_MAX], visible points are ~ proportional to zoom span. |
| 152 | const visibleRaw = Math.max(2, Math.floor(totalPoints * spanFrac)); |
| 153 | if (sampling === 'none') return visibleRaw; |
| 154 | |
| 155 | const target = computeZoomAwareTarget(samplingThreshold, spanFrac); |
| 156 | return Math.min(visibleRaw, target); |
| 157 | }; |
| 158 | |
| 159 | type RollingStat = Readonly<{ |
| 160 | push(value: number): void; |
no test coverage detected