"use strict"; const $ = (id) => document.getElementById(id); const fmt = (n, digits = 3) => n.toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits }); const caseNames = ['将黄色杯中的内容物倒入粉色碗中', '将杯中的内容物倒入碗中']; let data, abba; function drawStages(index) { const stages = data.history.map(s => ({ ...s, ms: s.cases[index].median_ms })); stages.push({ label: '全部无损优化', detail: '最终 ABBA · 固定 launcher', ms: data.cases[index].final_ms }); $('stage-bars').innerHTML = stages.map((s, i) => `
0${i + 1}
${s.label}${s.detail}
${fmt(s.ms, 1)}
`).join(''); } function drawAbba(index) { const c = abba.cases[index], w = 620, h = 212, left = 40, right = 12, top = 17, bottom = 27; const x = i => left + i / 29 * (w - left - right); const y = ms => top + (735 - ms) / 60 * (h - top - bottom); const grid = [680, 700, 720, 735].map(v => `${v}`).join(''); const ticks = [0, 9, 19, 29].map(i => `${i + 1}`).join(''); const series = (arr, color, label) => `${arr.map((s, i) => `${label} · 样本 ${i + 1} · ${fmt(s * 1000)} ms`).join('')}`; $('abba-chart').innerHTML = `ms${grid}${ticks}${series(c.baseline_seconds, '#76aaff', '固定 Sage2 基线')}${series(c.candidate_seconds, '#78efc0', '全部无损优化')}`; } function drawQuality(index) { const q = data.cases[index].quality; const rows = [ ['Joint MAE', q.joint_mae_rad, data.thresholds.joint_mae_rad, 'rad'], ['Joint max absolute', q.joint_max_abs_rad, data.thresholds.joint_max_abs_rad, 'rad'], ['Gripper max absolute', q.gripper_max_abs, data.thresholds.gripper_max_abs, ''], ]; $('quality-bars').innerHTML = rows.map(([name, value, threshold, unit]) => `
${name}${fmt(value, 5)} ${unit}
PASS · 阈值占用 ${fmt(value / threshold * 100, 1)}%≤ ${threshold} ${unit}
`).join(''); } function selectCase(index) { const c = data.cases[index]; document.querySelectorAll('[data-case]').forEach(b => b.setAttribute('aria-pressed', String(Number(b.dataset.case) === index))); document.querySelectorAll('[data-case-number]').forEach(n => n.textContent = '0' + (index + 1)); $('case-task').textContent = caseNames[index]; $('selected-speed').textContent = fmt(c.total_speedup); $('selected-original').textContent = fmt(c.original_ms) + ' ms'; $('selected-final').textContent = fmt(c.final_ms) + ' ms'; $('selected-saved').textContent = fmt(c.original_ms - c.final_ms) + ' ms'; $('selected-reduction').textContent = fmt(c.latency_reduction_pct, 2) + '%'; $('abba-before').textContent = fmt(c.sage_baseline_ms); $('abba-after').textContent = fmt(c.final_ms); $('abba-drop').textContent = fmt(c.lossless_reduction_pct) + '%'; $('abba-speed').textContent = fmt(c.lossless_speedup, 5) + '×'; $('recorded-change').textContent = (c.recorded_mae_degradation_rad < 0 ? '−' : '+') + fmt(Math.abs(c.recorded_mae_degradation_rad), 5) + ' rad'; drawStages(index); drawAbba(index); drawQuality(index); } Promise.all(['summary', 'abba'].map(name => fetch(`data/${name}.json`).then(r => { if (!r.ok) throw new Error(`Data request failed: ${r.status}`); return r.json(); }))).then(([summary, measurements]) => { data = summary; abba = measurements; document.querySelectorAll('[data-case]').forEach(b => b.addEventListener('click', () => selectCase(Number(b.dataset.case)))); selectCase(0); }).catch(() => { $('data-error').hidden = false; });