42 lines
1.9 KiB
TypeScript
42 lines
1.9 KiB
TypeScript
/** Geometry in rendered pixels, including the responsive SVG width. */
|
|
export function chartTimeAtPixel(pixel: number, duration: number, scale: number, ratio: number, padding: number): number {
|
|
return Math.max(0, Math.min(duration, duration - (pixel / Math.max(ratio, 0.001) - padding) / scale));
|
|
}
|
|
|
|
export function chartPixelAtTime(time: number, duration: number, scale: number, ratio: number, padding: number): number {
|
|
return (padding + (duration - Math.max(0, Math.min(duration, time))) * scale) * ratio;
|
|
}
|
|
|
|
/** Fixed-size overview; the final judgement belongs to the last bin. */
|
|
export function chartDensity(notes: readonly { timeSeconds: number }[], duration: number, bins = 120): number[] {
|
|
const counts = Array<number>(bins).fill(0);
|
|
for (const note of notes) {
|
|
const index = Math.max(0, Math.min(bins - 1, Math.floor(note.timeSeconds / Math.max(duration, 0.001) * bins)));
|
|
counts[index]++;
|
|
}
|
|
const maximum = Math.max(1, ...counts);
|
|
return counts.map(count => count / maximum);
|
|
}
|
|
|
|
export function chartColumnAtTime(time: number, duration: number, secondsPerColumn: number): number {
|
|
const count = Math.max(1, Math.ceil(duration / secondsPerColumn));
|
|
return Math.max(0, Math.min(count - 1, Math.floor(time / secondsPerColumn)));
|
|
}
|
|
|
|
export function chartColumnContains(time: number, start: number, end: number, duration: number): boolean {
|
|
return time >= start && (time < end || (time === duration && end >= duration));
|
|
}
|
|
|
|
/** Use actual SUS boundaries; silent bars and BPM changes still count. */
|
|
export function chartMeasureAtTime(measures: readonly { number: number; timeSeconds: number }[], time: number): number | null {
|
|
if (measures.length === 0 || time < measures[0].timeSeconds) return null;
|
|
let low = 0;
|
|
let high = measures.length - 1;
|
|
while (low < high) {
|
|
const middle = Math.ceil((low + high) / 2);
|
|
if (measures[middle].timeSeconds <= time) low = middle;
|
|
else high = middle - 1;
|
|
}
|
|
return measures[low].number;
|
|
}
|