Spaces:
Running
Running
File size: 8,821 Bytes
2ce6518 9dde5d4 2ce6518 9dde5d4 2ce6518 9dde5d4 2ce6518 9dde5d4 2ce6518 cbd6f7b 2ce6518 cbd6f7b 2ce6518 cbd6f7b 2ce6518 cbd6f7b 2ce6518 9dde5d4 2ce6518 9dde5d4 2ce6518 | 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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | /* Results / graphs tester β animates the paper's headline numbers the way the Manim results scene does:
* bars grow in and the paper's own method is highlighted with its delta over the best prior; line series
* draw left-to-right; metric cards count up. Reads the SAME chart spec the video's chart is built from
* (charts.ChartSpec.as_scene_dict: kind/categories/series/highlight/β¦), so the graph here equals the
* graph there. */
(function () {
const k = MANIMO.kit;
function norm(p) {
const kind = String(p.kind || p.primitive || "bar").toLowerCase();
const cats = (p.categories || p.labels || []).map(String);
let series = p.series || [];
if (!series.length && p.values) series = [{ name: "", values: p.values }];
series = series.map((s) => ({ name: String(s.name || ""), values: (s.values || []).map((v) => { const n = Number(v); return Number.isFinite(n) ? n : 0; }), points: s.points || null }));
return { kind, title: p.title || "", y: p.y_label || p.ylabel || "", unit: p.unit || "", highlight: p.highlight || null, cats, series, metrics: p.metrics || [] };
}
// ββ bar / line on a canvas ββββββββββββββββββββββββββββββββββββββββββββββββ
function mountChart(root, concept, theme) {
const spec = norm((concept && concept.params) || {});
const cv = document.createElement("canvas"); cv.width = 560; cv.height = 260; cv.className = "plot chart";
cv.setAttribute("role", "img"); cv.setAttribute("aria-label", (spec.title || "Results") + " " + spec.kind + " chart");
const log = k.panel("steps");
const isLine = spec.kind === "line";
const cats = spec.cats.length ? spec.cats : (spec.series[0] ? spec.series[0].values.map((_, i) => "x" + i) : []);
const vals = spec.series[0] ? spec.series[0].values : [];
const hiIdx = cats.indexOf(spec.highlight);
const best = bestOther(vals, hiIdx, cats);
function bars(shown, grow) {
const ctx = cv.getContext("2d"), W = cv.width, H = cv.height, n = cats.length, pad = 34;
ctx.fillStyle = theme.bg; ctx.fillRect(0, 0, W, H);
const mx = Math.max.apply(null, vals.concat([1e-6])) * 1.15, bw = (W - pad - 14) / Math.max(1, n);
ctx.strokeStyle = theme.muted; ctx.globalAlpha = 0.5; ctx.beginPath(); ctx.moveTo(pad, H - 26); ctx.lineTo(W - 6, H - 26); ctx.stroke(); ctx.globalAlpha = 1;
cats.forEach((c, i) => {
const full = (vals[i] || 0) / mx * (H - 56);
const h = i < shown ? full : (i === shown ? full * grow : 0);
const x = pad + i * bw + 6;
const hot = i === hiIdx;
ctx.fillStyle = hot ? theme.accent2 : theme.accent; ctx.globalAlpha = hot ? 1 : 0.85;
if (hot) { ctx.shadowColor = theme.accent2; ctx.shadowBlur = 16; }
ctx.fillRect(x, H - 26 - h, bw - 12, h); ctx.shadowBlur = 0; ctx.globalAlpha = 1;
ctx.fillStyle = theme.ink; ctx.font = "11px system-ui"; ctx.textAlign = "center";
ctx.fillText(c, x + (bw - 12) / 2, H - 12);
if (i < shown || (i === shown && grow > 0.98)) { ctx.fillStyle = hot ? theme.accent2 : theme.ink; ctx.font = "bold 12px system-ui"; ctx.fillText(k.fmt(vals[i]) + spec.unit, x + (bw - 12) / 2, H - 30 - h); }
});
if (spec.title) { ctx.fillStyle = theme.muted; ctx.font = "12px system-ui"; ctx.textAlign = "left"; ctx.fillText(spec.title + (spec.y ? " (" + spec.y + ")" : ""), pad, 16); }
}
function line(prog) {
const ctx = cv.getContext("2d"), W = cv.width, H = cv.height, pad = 34;
ctx.fillStyle = theme.bg; ctx.fillRect(0, 0, W, H);
const pts = (spec.series[0] && spec.series[0].points) || vals.map((v, i) => [i, v]);
if (!pts.length) { // empty/malformed series β draw the frame + a note, never index an empty array
if (spec.title) { ctx.fillStyle = theme.muted; ctx.font = "12px system-ui"; ctx.textAlign = "left"; ctx.fillText(spec.title + " β no data", pad, 16); }
return;
}
const xs = pts.map((q) => q[0]), ys = pts.map((q) => q[1]);
const xlo = Math.min.apply(null, xs), xhi = Math.max.apply(null, xs), ylo = Math.min.apply(null, ys), yhi = Math.max.apply(null, ys);
const X = (v) => pad + (v - xlo) / ((xhi - xlo) || 1) * (W - pad - 12);
const Y = (v) => H - 26 - (v - ylo) / ((yhi - ylo) || 1) * (H - 52);
ctx.strokeStyle = theme.muted; ctx.globalAlpha = .5; ctx.beginPath(); ctx.moveTo(pad, H - 26); ctx.lineTo(W - 6, H - 26); ctx.stroke(); ctx.globalAlpha = 1;
const upto = Math.max(1, Math.floor(pts.length * prog));
ctx.strokeStyle = theme.accent; ctx.lineWidth = 3; ctx.beginPath();
for (let i = 0; i < upto; i++) (i ? ctx.lineTo : ctx.moveTo).call(ctx, X(pts[i][0]), Y(pts[i][1]));
ctx.stroke();
const last = pts[upto - 1]; ctx.fillStyle = theme.accent2; ctx.beginPath(); ctx.arc(X(last[0]), Y(last[1]), 5, 0, 7); ctx.fill();
if (spec.title) { ctx.fillStyle = theme.muted; ctx.font = "12px system-ui"; ctx.textAlign = "left"; ctx.fillText(spec.title, pad, 16); }
}
let ctrl = null, raf = null;
if (isLine) {
root.appendChild(cv); root.appendChild(log);
k.logLine(log, "training curve β " + (spec.y || "value") + " over time", true); line(1);
function draw(done) { stop(); const t0 = performance.now(); (function tick(t) { const u = Math.min(1, (t - t0) / 2600); line(u); if (u >= 1) { raf = null; if (done) done(); return; } raf = requestAnimationFrame(tick); })(t0); }
function stop() { if (raf) { cancelAnimationFrame(raf); raf = null; } }
root.appendChild(k.row([k.button("βΆ Draw", () => draw())], "ctrls"));
return { play: (d) => draw(d), stop, reset: () => { stop(); line(1); } };
}
function render(step) {
bars(step, 1);
k.clear(log); log.appendChild(k.el("div", { class: "steps-h" }, [(spec.title || "results") + " (" + step + " / " + cats.length + ")"]));
for (let i = 0; i < step; i++) {
const tag = i === hiIdx ? " β this paper" : "";
k.logLine(log, cats[i] + " = <b>" + k.fmt(vals[i]) + spec.unit + "</b>" + tag, i === step - 1);
}
if (step >= cats.length && hiIdx >= 0 && best != null) {
const d = vals[hiIdx] - best.v;
k.logLine(log, "β² <b>" + (d >= 0 ? "+" : "") + k.fmt(d) + spec.unit + "</b> vs best prior (" + best.name + ")", true);
}
}
ctrl = k.stepper(new Array(cats.length), render);
root.appendChild(cv);
root.appendChild(k.row([k.button("βΈ Step", () => ctrl.next()), k.button("βΆ Play", () => ctrl.play(1050)), k.button("βΊ Reset", () => ctrl.reset(), "ghost")], "ctrls"));
root.appendChild(log);
return k.player(ctrl, 950);
}
// ββ metric cards (count up) βββββββββββββββββββββββββββββββββββββββββββββββ
function mountMetric(root, concept) {
const spec = norm((concept && concept.params) || {});
const wrap = k.el("div", { class: "metrics" });
const log = k.panel("steps");
root.appendChild(wrap); root.appendChild(log);
function render(step) {
k.clear(wrap); k.clear(log); log.appendChild(k.el("div", { class: "steps-h" }, ["headline metrics (" + step + " / " + spec.metrics.length + ")"]));
spec.metrics.slice(0, step).forEach((mtr, i) => {
wrap.appendChild(k.el("div", { class: "metric" }, [
k.el("div", { class: "m-val" }, [k.fmt(Number(mtr.value)) + (mtr.unit || "")]),
k.el("div", { class: "m-name" }, [String(mtr.name || "")]),
mtr.delta != null ? k.el("div", { class: "m-delta" }, ["β² " + String(mtr.delta)]) : "",
].filter(Boolean)));
k.logLine(log, (mtr.name || "metric") + " = <b>" + k.fmt(Number(mtr.value)) + (mtr.unit || "") + "</b>" + (mtr.delta != null ? " (" + mtr.delta + ")" : ""), i === step - 1);
});
}
const ctrl = k.stepper(new Array(spec.metrics.length), render);
root.appendChild(k.row([k.button("βΈ Step", () => ctrl.next()), k.button("βΆ Play", () => ctrl.play(1150)), k.button("βΊ Reset", () => ctrl.reset(), "ghost")], "ctrls"));
return k.player(ctrl, 1080);
}
function bestOther(vals, hiIdx, cats) {
let b = null;
vals.forEach((v, i) => { if (i !== hiIdx && (b == null || v > b.v)) b = { v, name: (cats && cats[i]) || ("#" + i) }; });
return b;
}
function mount(root, concept, theme) {
const kind = String(((concept && concept.params) || {}).kind || (concept && concept.primitive) || "bar").toLowerCase();
return kind === "metric" ? mountMetric(root, concept) : mountChart(root, concept, theme);
}
["chart", "results", "bar", "line", "metric", "graph"].forEach((key) => { MANIMO.registry[key] = { title: "Results", mount }; });
})();
|