{"spec_id":"line-tanabe-sugano","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// line-tanabe-sugano: Tanabe-Sugano Diagram for Crystal Field Theory\n// Library: chartjs 4.4.7 | JavaScript 22.23.3\n// Quality: 90/100 | Created: 2026-10-01\n//# anyplot-orientation: square\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data ------------------------------------------------------------------\n// d² (V³⁺) in an octahedral field at C/B = 4.42. Term energies come from the\n// Tanabe-Sugano matrices for d², in units of B with the Racah A dropped and\n// Dq = Δo/10: every cubic term is a 1x1 or 2x2 block that is diagonalised\n// exactly, so terms of the same symmetry and multiplicity (the two ³T₁g, the\n// ¹Eg/¹T₂g pairs) stay as avoided crossings instead of crossing.\nconst RACAH_C = 4.42;\nconst eigenvalues = (upper, lower, coupling) => {\n    const mean = (upper + lower) / 2;\n    const half = Math.hypot((upper - lower) / 2, coupling);\n    return [mean - half, mean + half];\n};\n\n// ³T₁g: t₂g²(-5B) and t₂g¹eg¹(+4B) mix through 6B; its lower root is the\n// ground term for every field strength, so d² has no high-spin/low-spin\n// crossover and the diagram needs no crossover line.\nconst groundTerm = (dq) => eigenvalues(-5 - 8 * dq, 4 + 2 * dq, 6)[0];\n\nconst terms = [\n    { label: \"³T₁g(F)\", spinAllowed: true, energy: groundTerm },\n    { label: \"³T₂g\", spinAllowed: true, energy: (dq) => -8 + 2 * dq },\n    { label: \"³T₁g(P)\", spinAllowed: true, energy: (dq) => eigenvalues(-5 - 8 * dq, 4 + 2 * dq, 6)[1] },\n    { label: \"³A₂g\", spinAllowed: true, energy: (dq) => -8 + 12 * dq },\n    {\n        label: \"¹Eg\",\n        spinAllowed: false,\n        energy: (dq) => eigenvalues(1 + 2 * RACAH_C - 8 * dq, 2 * RACAH_C + 12 * dq, 2 * Math.sqrt(3))[0],\n    },\n    {\n        label: \"¹T₂g\",\n        spinAllowed: false,\n        energy: (dq) => eigenvalues(1 + 2 * RACAH_C - 8 * dq, 2 * RACAH_C + 2 * dq, 2 * Math.sqrt(3))[0],\n    },\n    { label: \"¹T₁g\", spinAllowed: false, energy: (dq) => 4 + 2 * RACAH_C + 2 * dq },\n    {\n        label: \"¹A₁g\",\n        spinAllowed: false,\n        energy: (dq) =>\n            eigenvalues(10 + 5 * RACAH_C - 8 * dq, 8 + 4 * RACAH_C + 12 * dq, Math.sqrt(6) * (2 + RACAH_C))[0],\n    },\n];\n\n// Δo/B from 0 to 40 in 321 steps — dense enough to keep avoided crossings smooth\nconst fieldStrengths = Array.from({ length: 321 }, (_, i) => i / 8);\n\n// Imprint palette positions 1-8 in canonical order; spin-allowed terms (the ones\n// sharing the ground term's triplet multiplicity, so the strong absorptions) are\n// thick and solid, spin-forbidden singlets thin and dashed.\nconst datasets = terms.map((term, i) => ({\n    label: term.label,\n    data: fieldStrengths.map((fieldStrength) => ({\n        x: fieldStrength,\n        y: term.energy(fieldStrength / 10) - groundTerm(fieldStrength / 10),\n    })),\n    borderColor: t.palette[i],\n    borderWidth: term.spinAllowed ? 6 : 3,\n    borderDash: term.spinAllowed ? [] : [13, 10],\n    pointRadius: 0,\n    // the flat ground term runs along the x axis — let its full stroke draw\n    clip: 8,\n}));\n\n// --- Mount -----------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// Term symbols sit at the right end of their own curve instead of in a legend,\n// which keeps eight curves readable; labels are nudged apart vertically where\n// curves converge (¹Eg and ¹T₂g end ~0.2 E/B apart).\nconst termLabels = {\n    id: \"termLabels\",\n    afterDatasetsDraw(chart) {\n        const { ctx, chartArea, scales } = chart;\n        const labels = chart.data.datasets\n            .map((dataset) => ({\n                text: dataset.label,\n                color: dataset.borderColor,\n                y: scales.y.getPixelForValue(dataset.data[dataset.data.length - 1].y),\n            }))\n            .sort((a, b) => a.y - b.y);\n        labels.forEach((label, i) => {\n            if (i > 0) label.y = Math.max(label.y, labels[i - 1].y + 28);\n        });\n\n        ctx.save();\n        ctx.font = \"600 20px Helvetica, Arial, sans-serif\";\n        ctx.textBaseline = \"middle\";\n        labels.forEach((label) => {\n            ctx.fillStyle = label.color;\n            ctx.fillText(label.text, chartArea.right + 14, label.y);\n        });\n        ctx.restore();\n    },\n};\n\n// --- Chart -----------------------------------------------------------------\nnew Chart(canvas, {\n    type: \"line\",\n    data: { datasets },\n    options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        animation: false,\n        layout: { padding: { right: 120, top: 6, bottom: 6, left: 6 } },\n        plugins: {\n            title: {\n                display: true,\n                text: \"line-tanabe-sugano · javascript · chartjs · anyplot.ai\",\n                color: t.ink,\n                font: { size: 26, weight: \"500\" },\n                padding: { bottom: 4 },\n            },\n            subtitle: {\n                display: true,\n                text: \"d² (V³⁺) in Oₕ, C/B = 4.42 · solid: spin-allowed triplets · dashed: spin-forbidden singlets\",\n                color: t.inkSoft,\n                font: { size: 18 },\n                padding: { bottom: 20 },\n            },\n            legend: { display: false },\n        },\n        scales: {\n            x: {\n                type: \"linear\",\n                min: 0,\n                max: 40,\n                title: { display: true, text: \"Δₒ/B — reduced ligand-field strength\", color: t.ink, font: { size: 19 } },\n                ticks: { color: t.inkSoft, font: { size: 16 }, stepSize: 5, padding: 8 },\n                grid: { color: t.grid, tickLength: 0 },\n                border: { color: t.grid },\n            },\n            y: {\n                min: 0,\n                max: 80,\n                title: { display: true, text: \"E/B — term energy above ground term\", color: t.ink, font: { size: 19 } },\n                ticks: { color: t.inkSoft, font: { size: 16 }, stepSize: 10, padding: 8 },\n                grid: { color: t.grid, tickLength: 0 },\n                border: { color: t.grid },\n            },\n        },\n    },\n    plugins: [termLabels],\n});\n"}