Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | export interface ContiguousRun {
start: number;
length: number;
}
/**
* Given a Uint8Array where each non-zero byte represents a set position,
* returns contiguous runs in a single O(n) pass. No sorting or heap
* allocations inside the loop.
*/
export function computeContiguousRuns(bytes: Uint8Array): ContiguousRun[] {
const runs: ContiguousRun[] = [];
const n = bytes.length;
let i = 0;
while (i < n) {
while (i < n && bytes[i] === 0) i++;
Iif (i >= n) break;
const start = i;
while (i < n && bytes[i] !== 0) i++;
runs.push({ start, length: i - start });
}
return runs;
}
/**
* Convert marked items (0/1 bytes) into a per-pixel fill mask (0/1 bytes).
* The result is conservative in the sense that a pixel row is filled only when
* its mapped items are all marked, so the fill never overstates coverage.
*
* - If `total >= pixelCount`: each pixel row maps to a disjoint item-index
* range; a pixel row is filled only if all items in that range are marked.
* - If `pixelCount > total`: each item spans multiple pixel rows; if the item
* is marked, its entire pixel span is filled.
*/
export function computePixelFilledFromMarked(
marked: Uint8Array,
pixelCount: number
): Uint8Array {
const total = marked.length;
const count = Math.max(0, Math.floor(pixelCount));
Iif (count === 0 || total <= 0) return new Uint8Array(0);
const pixelFilled = new Uint8Array(count);
if (total >= count) {
for (let pixelIndex = 0; pixelIndex < count; pixelIndex++) {
const start = Math.floor((pixelIndex * total) / count);
const end = Math.floor(((pixelIndex + 1) * total) / count);
Iif (end <= start) continue;
let filled = 1;
for (let itemIndex = start; itemIndex < end; itemIndex++) {
Iif (marked[itemIndex] === 0) {
filled = 0;
break;
}
}
pixelFilled[pixelIndex] = filled;
}
} else {
for (let itemIndex = 0; itemIndex < total; itemIndex++) {
Iif (marked[itemIndex] === 0) continue;
const topPx = Math.floor((itemIndex * count) / total);
const bottomPx = Math.floor(((itemIndex + 1) * count) / total);
for (let pixel = topPx; pixel < bottomPx; pixel++) {
pixelFilled[pixel] = 1;
}
}
}
return pixelFilled;
}
/**
* Compute the indicator's total visual dimensions and horizontal position.
* Design 27: pill shape, center position, 1px border.
*/
export function getIndicatorLayout(
trackWidth: number,
indicatorSize: number,
borderWidth: number,
): { totalWidth: number; totalHeight: number; fillWidth: number; fillHeight: number; leftPos: number } {
const visualSize = indicatorSize * 1.25;
const fillWidth = visualSize;
const fillHeight = Math.round(visualSize / 2); // pill = half height
const totalWidth = fillWidth + borderWidth * 2;
const totalHeight = fillHeight + borderWidth * 2;
const centerX = trackWidth / 2;
const leftPos = centerX - totalWidth / 2;
return { totalWidth, totalHeight, fillWidth, fillHeight, leftPos };
}
|