// Code4Scene takeaway: requirement-family scores per agent (paper Figure 14b), in the paper figures' kit style -- // grouped bars in Text-to-Scene rank order, Spatial Composition in the agent's company colour, the other families grey. // Generated by scripts/kit_charts.py; the layout follows figure-kit/takeaway.py and the leaderboard bars. import {bindRecordTooltip} from "./chart-interactions.js?v=20261004kit"; const D = {"rows": [{"name": "Claude Fable 5.1 (max)", "org": "Anthropic", "colour": "#D97757", "fam": [0.906, 0.738, 0.67, 0.538]}, {"name": "GPT-6 Astra (max)", "org": "OpenAI", "colour": "#10A37F", "fam": [0.875, 0.715, 0.643, 0.543]}, {"name": "Claude Opus 5 (max)", "org": "Anthropic", "colour": "#D97757", "fam": [0.803, 0.624, 0.573, 0.479]}, {"name": "GPT-5.6 Sol (high)", "org": "OpenAI", "colour": "#10A37F", "fam": [0.75, 0.66, 0.602, 0.469]}, {"name": "Gemini 3.8 Flash (high)", "org": "Google", "colour": "#7b1fa2", "fam": [0.814, 0.639, 0.572, 0.473]}, {"name": "Muse Spark 1.3 (medium)", "org": "Meta", "colour": "#42a5f5", "fam": [0.73, 0.641, 0.546, 0.393]}, {"name": "Grok 4.6 (high)", "org": "xAI", "colour": "#111111", "fam": [0.647, 0.558, 0.457, 0.334]}, {"name": "Qwen 3.8 27B (thinking off)", "org": "Alibaba", "colour": "#fe7016", "fam": [0.724, 0.52, 0.428, 0.3]}, {"name": "Qwen 3.8 27B (thinking on)", "org": "Alibaba", "colour": "#fe7016", "fam": [0.66, 0.525, 0.47, 0.307]}, {"name": "GLM-5.3 Flash (max)", "org": "Z.ai", "colour": "#96650b", "fam": [0.467, 0.421, 0.377, 0.299]}, {"name": "Gemma 4 31B (thinking on)", "org": "Google", "colour": "#7b1fa2", "fam": [0.561, 0.407, 0.387, 0.272]}, {"name": "Gemma 4 31B (thinking off)", "org": "Google", "colour": "#7b1fa2", "fam": [0.612, 0.453, 0.387, 0.252]}, {"name": "Inkling (high)", "org": "Thinking Machines", "colour": "#686868", "fam": [0.572, 0.394, 0.361, 0.193]}, {"name": "DeepSeek V4.1 Flash (high)", "org": "DeepSeek", "colour": "#3f51e0", "fam": [0.294, 0.215, 0.259, 0.12]}], "families": ["Identity & Environment", "Content & Quantity", "Attributes & Materials", "Spatial Composition"], "greys": ["#dedede", "#c6c6c6", "#a9a9a9"]}; const LOGOS = {"Anthropic": {"icon": "anthropic", "fill": "#141413", "viewBox": "0 0 24 24", "inner": "", "rule": "evenodd"}, "OpenAI": {"icon": "openai", "fill": "#141413", "viewBox": "0 0 24 24", "inner": "", "rule": "evenodd"}, "Meta": {"icon": "meta-color", "viewBox": "0 0 24 24", "inner": "", "rule": null}, "Google": {"icon": "google-color", "viewBox": "0 0 24 24", "inner": "", "rule": null}, "DeepSeek": {"icon": "deepseek-color", "viewBox": "0 0 24 24", "inner": "", "rule": null}, "xAI": {"icon": "xai", "tile": "#111111", "shape": "square", "viewBox": "0 0 24 24", "inner": "", "rule": "evenodd"}, "Z.ai": {"icon": "zai", "tile": "#111111", "shape": "square", "viewBox": "0 0 24 24", "inner": "", "rule": "evenodd"}, "Alibaba": {"icon": "qwen-color", "viewBox": "0 0 24 24", "inner": "", "rule": null}, "Thinking Machines": {"wordmark": ["THINKING", "MACHINES"]}}; await Promise.all(['400 13px Inter', '700 13px Inter', '500 12.5px "IBM Plex Mono"'].map(f => document.fonts.load(f))); const svg = document.getElementById('tk-families'), W = 1200, H = 410, NS = 'http://www.w3.org/2000/svg'; const SANS = 'Inter, Arial, sans-serif', MONO = '"IBM Plex Mono", monospace'; const G = {left: 40, right: 24, top: 34, base: 262, groupFill: 0.80, gap: 2, radius: 3.5, logo: 18, logoGap: 8, nameGap: 9, nameAngle: -60, nameSize: 13, nameLead: 14.5, nameOneLine: 97, nameLine: 93, nameTol: 12}; function el(tag, attrs, parent) { const e = document.createElementNS(NS, tag); for (const k in attrs) e.setAttribute(k, attrs[k]); (parent || svg).appendChild(e); return e; } function text(x, y, s, attrs, parent) { const t = el('text', Object.assign({x, y}, attrs), parent); t.textContent = s; return t; } const ctx = document.createElement('canvas').getContext('2d'); function width(t, font) { ctx.font = font; return ctx.measureText(t).width; } function wrap(name, font) { if (width(name, font) <= G.nameOneLine) return [name]; const w = name.split(' '); const splits = k => { const out = []; if (k === 2) for (let i = 1; i < w.length; i++) out.push([w.slice(0, i), w.slice(i)]); if (k === 3) for (let i = 1; i < w.length; i++) for (let j = i + 1; j < w.length; j++) out.push([w.slice(0, i), w.slice(i, j), w.slice(j)]); return out.map(p => p.map(x => x.join(' '))); }; for (const k of [2, 3]) { const c = splits(k).map(lines => { const ws = lines.map(l => width(l, font)); return {lines, worst: Math.max(...ws), last: ws[ws.length - 1]}; }) .filter(o => o.worst <= G.nameLine); if (!c.length) continue; const floor = Math.min(...c.map(o => o.worst)); return c.filter(o => o.worst <= floor + G.nameTol).sort((a, b) => b.last - a.last)[0].lines; } return [name]; } function logo(org, cx, ly, parent) { const L = LOGOS[org], s = G.logo, lx = cx - s / 2; if (L.wordmark) { L.wordmark.forEach((w, k) => text(cx, ly + 7.5 + k * 6.2, w, {'text-anchor': 'middle', 'font-family': SANS, 'font-weight': 500, 'font-size': 5.2, 'letter-spacing': 0.35, fill: '#4a4a4a'}, parent)); return; } let pad = 0, fill = L.fill || null; if (L.tile) { if (L.shape === 'circle') el('circle', {cx, cy: ly + s / 2, r: s / 2, fill: L.tile}, parent); else el('rect', {x: lx, y: ly, width: s, height: s, rx: 3.2, fill: L.tile}, parent); pad = 3.4; fill = '#ffffff'; } const ic = el('svg', {x: lx + pad, y: ly + pad, width: s - 2 * pad, height: s - 2 * pad, viewBox: L.viewBox}, parent); if (fill) { ic.setAttribute('fill', fill); ic.setAttribute('color', fill); } if (L.rule) ic.setAttribute('fill-rule', L.rule); ic.innerHTML = L.inner; } const y = v => G.base - v / 100 * (G.base - G.top); for (const v of [25, 50, 75, 100]) el('line', {x1: G.left - 8, x2: W - G.right, y1: y(v), y2: y(v), stroke: '#cfcfcf', 'stroke-width': 1.4, 'stroke-dasharray': '0.1 4.2', 'stroke-linecap': 'round'}); el('line', {x1: G.left - 8, x2: W - G.right, y1: G.base, y2: G.base, stroke: '#e6e6e6', 'stroke-width': 1}); const n = D.rows.length, pitch = (W - G.left - G.right) / n, gw = pitch * G.groupFill, bw = (gw - 3 * G.gap) / 4; D.rows.forEach((d, i) => { const cx = G.left + (i + 0.5) * pitch, g0 = cx - gw / 2; const node = el('g', {class: 'bar-node'}); el('rect', {x: G.left + i * pitch, y: G.top - 20, width: pitch, height: H - G.top + 20, fill: 'transparent', class: 'bar-hit'}, node); d.fam.forEach((v, j) => { const x0 = g0 + j * (bw + G.gap), top = y(v * 100), r = Math.min(G.radius, bw / 2); el('path', {class: j === 3 ? 'bar' : 'bar fam', d: `M${x0},${G.base} V${top + r} Q${x0},${top} ${x0 + r},${top} H${x0 + bw - r} Q${x0 + bw},${top} ${x0 + bw},${top + r} V${G.base} Z`, fill: j === 3 ? d.colour : D.greys[j]}, node); if (j === 3) text(x0 + bw / 2, top - 6, String(Math.round(v * 100)), {'text-anchor': 'middle', 'font-family': MONO, 'font-weight': 500, 'font-size': 12.5, fill: '#262626'}, node); }); logo(d.org, cx, G.base + G.logoGap, node); const font = `400 ${G.nameSize}px ${SANS}`, lines = wrap(d.name, font); const g = el('g', {transform: `translate(${cx + 3},${G.base + G.logoGap + G.logo + G.nameGap}) rotate(${G.nameAngle})`}, node); const off = -(lines.length - 1) / 2 * G.nameLead; lines.forEach((ln, k) => text(0, off + k * G.nameLead + 4.5, ln, {'text-anchor': 'end', 'font-family': SANS, 'font-size': G.nameSize, fill: '#262626'}, g)); bindRecordTooltip(node, svg, {name: d.name, provider: d.org, metrics: D.families.map((f, j) => ({label: f, value: d.fam[j].toFixed(3)}))}, '.bar:not(.fam)'); }); svg.dataset.ready = 'true';