( data: ReadonlyArray<DataPoint>, targetPoints: number, mode: BucketMode )
| 23 | type BucketMode = 'average' | 'max' | 'min'; |
| 24 | |
| 25 | function sampleByBuckets( |
| 26 | data: ReadonlyArray<DataPoint>, |
| 27 | targetPoints: number, |
| 28 | mode: BucketMode |
| 29 | ): ReadonlyArray<DataPoint> { |
| 30 | const n = data.length; |
| 31 | const threshold = clampTargetPoints(targetPoints); |
| 32 | |
| 33 | if (threshold <= 0 || n === 0) return []; |
| 34 | if (threshold === 1) return [data[0]!]; |
| 35 | if (threshold === 2) return n >= 2 ? [data[0]!, data[n - 1]!] : [data[0]!]; |
| 36 | if (n <= threshold) return data; |
| 37 | |
| 38 | const lastIndex = n - 1; |
| 39 | const out = new Array<DataPoint>(threshold); |
| 40 | out[0] = data[0]!; |
| 41 | out[threshold - 1] = data[lastIndex]!; |
| 42 | |
| 43 | const bucketSize = (n - 2) / (threshold - 2); |
| 44 | |
| 45 | for (let bucket = 0; bucket < threshold - 2; bucket++) { |
| 46 | let rangeStart = Math.floor(bucketSize * bucket) + 1; |
| 47 | let rangeEndExclusive = Math.min(Math.floor(bucketSize * (bucket + 1)) + 1, lastIndex); |
| 48 | |
| 49 | if (rangeStart >= rangeEndExclusive) { |
| 50 | rangeStart = Math.min(rangeStart, lastIndex - 1); |
| 51 | rangeEndExclusive = Math.min(rangeStart + 1, lastIndex); |
| 52 | } |
| 53 | |
| 54 | let chosen: DataPoint | null = null; |
| 55 | |
| 56 | if (mode === 'average') { |
| 57 | let sumX = 0; |
| 58 | let sumY = 0; |
| 59 | let sumSize = 0; |
| 60 | let count = 0; |
| 61 | let sizeCount = 0; |
| 62 | for (let i = rangeStart; i < rangeEndExclusive; i++) { |
| 63 | const p = data[i]!; |
| 64 | const { x, y } = getXY(p); |
| 65 | if (!Number.isFinite(x) || !Number.isFinite(y)) continue; |
| 66 | sumX += x; |
| 67 | sumY += y; |
| 68 | count++; |
| 69 | |
| 70 | const size = getSize(p); |
| 71 | if (typeof size === 'number' && Number.isFinite(size)) { |
| 72 | sumSize += size; |
| 73 | sizeCount++; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | if (count > 0) { |
| 78 | const avgX = sumX / count; |
| 79 | const avgY = sumY / count; |
| 80 | if (sizeCount > 0) { |
| 81 | chosen = [avgX, avgY, sumSize / sizeCount]; |
| 82 | } else { |
no test coverage detected