{"spec_id":"phase-diagram-pt","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// phase-diagram-pt: Thermodynamic Phase Diagram (Pressure-Temperature)\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 91/100 | Created: 2026-06-08\n\nconst t = window.ANYPLOT_TOKENS;\n\n// CO2 phase diagram constants (pressures in atm, temperatures in K)\nconst R     = 8.314;\nconst T_tp  = 216.55, P_tp  = 5.18;   // triple point\nconst T_cp  = 304.13, P_cp  = 72.8;   // critical point\nconst H_sub = 25200;                   // J/mol — sublimation enthalpy\nconst H_vap = 16500;                   // J/mol — effective vaporization enthalpy\nconst dPdT  = 108;                     // atm/K  — CO2 fusion curve slope (positive)\n\n// Sublimation curve (solid ↔ gas): 150 K → triple point\nconst sublimation = [];\nfor (let T = 150; T <= T_tp; T += 1.5) {\n  sublimation.push({ x: T, y: P_tp * Math.exp((H_sub / R) * (1 / T_tp - 1 / T)) });\n}\nsublimation.push({ x: T_tp, y: P_tp });\n\n// Vaporization curve (liquid ↔ gas): triple point → critical point\nconst vaporization = [{ x: T_tp, y: P_tp }];\nfor (let T = T_tp + 2; T <= T_cp; T += 2) {\n  vaporization.push({ x: T, y: P_tp * Math.exp((H_vap / R) * (1 / T_tp - 1 / T)) });\n}\nvaporization.push({ x: T_cp, y: P_cp });\n\n// Fusion curve (solid ↔ liquid): steep positive slope for CO2\nconst fusion = [];\nfor (let i = 0; i <= 24; i++) {\n  const P = P_tp + i * 21;\n  fusion.push({ x: T_tp + (P - P_tp) / dPdT, y: P });\n}\n\n// Mount canvas\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// Plugin: draw phase-region labels directly on the canvas\nconst regionPlugin = {\n  id: \"phaseRegions\",\n  afterDraw({ ctx, scales: { x, y } }) {\n    const regions = [\n      { lines: [\"SOLID\"],                T: 176,  P: 100   },\n      { lines: [\"LIQUID\"],               T: 248,  P: 32    },\n      { lines: [\"GAS\"],                  T: 278,  P: 0.05  },\n      { lines: [\"SUPER\", \"CRITICAL\"],     T: 322,  P: 260   },\n    ];\n    ctx.save();\n    ctx.font = \"bold 20px sans-serif\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    ctx.globalAlpha = 0.5;\n    ctx.fillStyle = t.inkSoft;\n    const lineH = 26;\n    for (const { lines, T, P } of regions) {\n      const px = x.getPixelForValue(T);\n      const py = y.getPixelForValue(P);\n      const offset = (lines.length - 1) * lineH / 2;\n      lines.forEach((line, i) => ctx.fillText(line, px, py - offset + i * lineH));\n    }\n    ctx.restore();\n  },\n};\n\n// Plugin: draw only bottom + left border lines (L-shape)\nconst lFramePlugin = {\n  id: \"lFrame\",\n  afterDraw({ ctx, chartArea: { top, right, bottom, left } }) {\n    ctx.save();\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(left, top);\n    ctx.lineTo(left, bottom);\n    ctx.lineTo(right, bottom);\n    ctx.stroke();\n    ctx.restore();\n  },\n};\n\nconst title = \"CO₂ Phase Diagram · phase-diagram-pt · javascript · chartjs · anyplot.ai\";\nconst titleSize = Math.max(16, Math.round(22 * 67 / title.length));\n\nnew Chart(canvas, {\n  type: \"scatter\",\n  plugins: [regionPlugin, lFramePlugin],\n  data: {\n    datasets: [\n      {\n        label: \"Solid–Gas (Sublimation)\",\n        data: sublimation,\n        borderColor: t.palette[0],\n        backgroundColor: \"transparent\",\n        showLine: true,\n        borderWidth: 3.5,\n        pointRadius: 0,\n      },\n      {\n        label: \"Liquid–Gas (Vaporization)\",\n        data: vaporization,\n        borderColor: t.palette[1],\n        backgroundColor: \"transparent\",\n        showLine: true,\n        borderWidth: 3.5,\n        pointRadius: 0,\n      },\n      {\n        label: \"Solid–Liquid (Fusion)\",\n        data: fusion,\n        borderColor: t.palette[2],\n        backgroundColor: \"transparent\",\n        showLine: true,\n        borderWidth: 3.5,\n        pointRadius: 0,\n      },\n      {\n        label: `Triple Point  ${T_tp} K, ${P_tp} atm`,\n        data: [{ x: T_tp, y: P_tp }],\n        backgroundColor: t.palette[3],\n        borderColor: t.ink,\n        borderWidth: 1,\n        pointRadius: 13,\n        pointHoverRadius: 15,\n        pointStyle: \"star\",\n        showLine: false,\n      },\n      {\n        label: `Critical Point  ${T_cp} K, ${P_cp} atm`,\n        data: [{ x: T_cp, y: P_cp }],\n        backgroundColor: t.palette[4],\n        borderColor: t.ink,\n        borderWidth: 1,\n        pointRadius: 13,\n        pointHoverRadius: 15,\n        pointStyle: \"rectRot\",\n        showLine: false,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 10, right: 40, bottom: 10, left: 10 } },\n    plugins: {\n      title: {\n        display: true,\n        text: title,\n        color: t.ink,\n        font: { size: titleSize, weight: \"500\" },\n        padding: { top: 10, bottom: 22 },\n      },\n      legend: {\n        position: \"bottom\",\n        labels: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          usePointStyle: true,\n          padding: 24,\n          boxWidth: 20,\n        },\n      },\n    },\n    scales: {\n      x: {\n        type: \"linear\",\n        min: 140,\n        max: 340,\n        title: {\n          display: true,\n          text: \"Temperature (K)\",\n          color: t.ink,\n          font: { size: 18 },\n          padding: { top: 10 },\n        },\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          stepSize: 20,\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n      },\n      y: {\n        type: \"logarithmic\",\n        min: 0.005,\n        max: 700,\n        title: {\n          display: true,\n          text: \"Pressure (atm)\",\n          color: t.ink,\n          font: { size: 18 },\n          padding: { bottom: 10 },\n        },\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          callback(value) {\n            const pow = Math.round(Math.log10(value));\n            const expected = Math.pow(10, pow);\n            if (Math.abs(value - expected) / expected < 0.002) {\n              return expected < 1 ? expected.toFixed(-pow) : String(Math.round(expected));\n            }\n            return \"\";\n          },\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n      },\n    },\n  },\n});\n"}