feat: show cumulative maximum score probability in timeline tooltip
Publish GitHub Pages output / publish (push) Successful in 56s
Publish GitHub Pages output / publish (push) Successful in 56s
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import type { NoteScorePoint } from '$lib/engine/index.ts';
|
||||
|
||||
export let notes: readonly NoteScorePoint[];
|
||||
export let maximumScoreProbabilities: readonly number[] = [];
|
||||
export let durationSeconds: number;
|
||||
export let minimumWidth = 720;
|
||||
export let timeTicks: readonly number[] = [];
|
||||
@@ -10,6 +11,7 @@
|
||||
timeSeconds: number;
|
||||
averageScore: number;
|
||||
maximumScore: number;
|
||||
maximumProbability?: number;
|
||||
};
|
||||
|
||||
const HEIGHT = 220;
|
||||
@@ -25,7 +27,7 @@
|
||||
|
||||
$: viewWidth = Math.max(320, chartWidth);
|
||||
$: plotWidth = viewWidth - LEFT - RIGHT;
|
||||
$: points = buildPoints(notes);
|
||||
$: points = buildPoints(notes, maximumScoreProbabilities);
|
||||
$: finalMaximumScore = Math.max(1, points.at(-1)?.maximumScore ?? 0);
|
||||
$: chartMaximumScore = Math.max(500_000, Math.ceil(finalMaximumScore / 500_000) * 500_000);
|
||||
$: averagePath = linePath(points, 'averageScore', durationSeconds, chartMaximumScore, viewWidth);
|
||||
@@ -41,14 +43,14 @@
|
||||
tooltipPercent = 0;
|
||||
}
|
||||
|
||||
function buildPoints(source: readonly NoteScorePoint[]): ScorePoint[] {
|
||||
function buildPoints(source: readonly NoteScorePoint[], probabilities: readonly number[]): ScorePoint[] {
|
||||
let averageScore = 0;
|
||||
let maximumScore = 0;
|
||||
const result: ScorePoint[] = [{ timeSeconds: 0, averageScore: 0, maximumScore: 0 }];
|
||||
for (const note of source) {
|
||||
const result: ScorePoint[] = [{ timeSeconds: 0, averageScore: 0, maximumScore: 0, maximumProbability: 1 }];
|
||||
for (const [index, note] of source.entries()) {
|
||||
averageScore += note.expectedScore;
|
||||
maximumScore += note.theoreticalMaxScore;
|
||||
result.push({ timeSeconds: note.timeSeconds, averageScore, maximumScore });
|
||||
result.push({ timeSeconds: note.timeSeconds, averageScore, maximumScore, maximumProbability: probabilities[index] });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -114,6 +116,12 @@
|
||||
return Math.round(score).toLocaleString('ko-KR');
|
||||
}
|
||||
|
||||
function formatProbability(probability: number | undefined): string {
|
||||
if (probability === undefined) return '—';
|
||||
if (probability > 0 && probability < 0.000001) return '<0.0001%';
|
||||
return `${(probability * 100).toLocaleString('ko-KR', { maximumFractionDigits: probability < 0.0001 ? 4 : 2 })}%`;
|
||||
}
|
||||
|
||||
function formatAxisScore(score: number): string {
|
||||
return score === 0 ? '0' : `${Math.round(score / 10_000).toLocaleString('ko-KR')}만`;
|
||||
}
|
||||
@@ -147,7 +155,7 @@
|
||||
aria-valuemin="0"
|
||||
aria-valuemax={Math.ceil(durationSeconds)}
|
||||
aria-valuenow={Math.round(hoverTime ?? 0)}
|
||||
aria-valuetext={hoveredPoint && hoverTime !== null ? `${formatTime(hoverTime)}, 평균 점수 ${formatScore(hoveredPoint.averageScore)}, 최고 점수 ${formatScore(hoveredPoint.maximumScore)}` : '방향키로 시각을 선택하세요'}
|
||||
aria-valuetext={hoveredPoint && hoverTime !== null ? `${formatTime(hoverTime)}, 평균 점수 ${formatScore(hoveredPoint.averageScore)}, 최고 점수 ${formatScore(hoveredPoint.maximumScore)}, 이 시간까지 최고점 유지 확률 ${formatProbability(hoveredPoint.maximumProbability)}` : '방향키로 시각을 선택하세요'}
|
||||
tabindex="0"
|
||||
on:pointermove={onPointerMove}
|
||||
on:pointerleave={() => hoverTime = null}
|
||||
@@ -176,10 +184,11 @@
|
||||
</svg>
|
||||
|
||||
{#if hoveredPoint && hoverTime !== null}
|
||||
<div class:align-right={tooltipPercent > 68} class="tooltip" style={`left: ${tooltipPercent}%;`} role="status">
|
||||
<div class:align-right={tooltipPercent > 50} class="tooltip" style={`left: ${tooltipPercent}%;`} role="status">
|
||||
<strong>{formatTime(hoverTime)}</strong>
|
||||
<span><i class="average"></i>평균 점수 <b>{formatScore(hoveredPoint.averageScore)}</b></span>
|
||||
<span><i class="maximum"></i>최고 점수 <b>{formatScore(hoveredPoint.maximumScore)}</b></span>
|
||||
<span>이 시간까지 최고점 유지 확률 <b>{formatProbability(hoveredPoint.maximumProbability)}</b></span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user