{"spec_id":"line-parametric","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// line-parametric: Parametric Curve Plot\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 93/100 | Created: 2026-06-20\n\nconst t = window.ANYPLOT_TOKENS;\nconst N = 600;\nconst TWO_PI = 2 * Math.PI;\nconst cs = t.seq[0];\nconst ce = t.seq[1];\n\n// Pre-parse gradient endpoints for inline lerp in segment callback\nconst sr = parseInt(cs.slice(1, 3), 16);\nconst sg = parseInt(cs.slice(3, 5), 16);\nconst sb = parseInt(cs.slice(5, 7), 16);\nconst er = parseInt(ce.slice(1, 3), 16);\nconst eg = parseInt(ce.slice(3, 5), 16);\nconst eb = parseInt(ce.slice(5, 7), 16);\n\n// Lissajous figure: 3:2 frequency ratio, phase shift π/4\nconst lissData = [];\nfor (let i = 0; i < N; i++) {\n  const ti = TWO_PI * i / (N - 1);\n  lissData.push({ x: Math.sin(3 * ti + Math.PI / 4), y: Math.sin(2 * ti) });\n}\n\n// Archimedean spiral: 3 revolutions, radius normalized to [0, 1]\nconst spiralData = [];\nfor (let i = 0; i < N; i++) {\n  const ti = 3 * TWO_PI * i / (N - 1);\n  spiralData.push({ x: (ti / (3 * TWO_PI)) * Math.cos(ti), y: (ti / (3 * TWO_PI)) * Math.sin(ti) });\n}\n\nconst container = document.getElementById(\"container\");\ncontainer.style.cssText = `\n  display: flex;\n  flex-direction: column;\n  background: ${t.pageBg};\n  padding: 0 40px;\n  box-sizing: border-box;\n`;\n\nconst titleDiv = document.createElement(\"div\");\ntitleDiv.style.cssText = `\n  text-align: center;\n  color: ${t.ink};\n  font: 600 22px/1.4 sans-serif;\n  padding: 18px 0 6px;\n  flex-shrink: 0;\n`;\ntitleDiv.textContent = \"line-parametric · javascript · chartjs · anyplot.ai\";\ncontainer.appendChild(titleDiv);\n\nconst row = document.createElement(\"div\");\nrow.style.cssText = `\n  display: flex;\n  flex: 1;\n  justify-content: space-evenly;\n  align-items: center;\n`;\ncontainer.appendChild(row);\n\nconst legendDiv = document.createElement(\"div\");\nlegendDiv.style.cssText = `\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 32px;\n  padding: 8px 0 14px;\n  flex-shrink: 0;\n`;\nlegendDiv.innerHTML = `\n  <div style=\"display:flex;align-items:center;gap:10px;font:14px sans-serif;color:${t.inkSoft};\">\n    <span style=\"display:inline-block;width:90px;height:5px;background:linear-gradient(to right,${cs},${ce});border-radius:3px;\"></span>\n    <span>color encodes direction of t: start &#8594; end</span>\n  </div>\n  <div style=\"display:flex;align-items:center;gap:8px;font:14px sans-serif;color:${t.inkSoft};\">\n    <span style=\"display:inline-block;width:11px;height:11px;border-radius:50%;background:${cs};border:2px solid ${t.pageBg};\"></span>\n    <span>t = 0</span>\n    <span style=\"display:inline-block;width:11px;height:11px;border-radius:50%;background:${ce};border:2px solid ${t.pageBg};margin-left:10px;\"></span>\n    <span>t = T</span>\n  </div>\n`;\ncontainer.appendChild(legendDiv);\n\n// Plugin: remove top/right chart borders, draw only bottom + left (L-shaped frame)\nconst lShapePlugin = {\n  id: \"lShape\",\n  afterDraw(chart) {\n    const { ctx, chartArea: { left, right, top, bottom } } = chart;\n    ctx.save();\n    // Erase top and right axis borders with background color\n    ctx.fillStyle = t.pageBg;\n    ctx.fillRect(left, top - 2, right - left + 2, 3);\n    ctx.fillRect(right - 1, top, 3, bottom - top + 2);\n    // Draw explicit bottom and left spines\n    ctx.strokeStyle = t.inkSoft;\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(left, bottom);\n    ctx.lineTo(right, bottom);\n    ctx.moveTo(left, top);\n    ctx.lineTo(left, bottom);\n    ctx.stroke();\n    ctx.restore();\n  },\n};\n\nconst panels = [\n  { data: lissData, subtitle: \"Lissajous — x = sin(3t + π/4), y = sin(2t)\", range: [-1.25, 1.25] },\n  { data: spiralData, subtitle: \"Archimedean Spiral — 3 revolutions\", range: [-1.1, 1.1] },\n];\n\nfor (const { data, subtitle, range } of panels) {\n  const div = document.createElement(\"div\");\n  div.style.cssText = \"width: 700px; height: 700px;\";\n  const canvas = document.createElement(\"canvas\");\n  div.appendChild(canvas);\n  row.appendChild(div);\n\n  new Chart(canvas, {\n    type: \"scatter\",\n    plugins: [lShapePlugin],\n    data: {\n      datasets: [\n        {\n          label: subtitle,\n          data,\n          showLine: true,\n          pointRadius: 0,\n          borderWidth: 2.5,\n          segment: {\n            borderColor: ctx => {\n              const ratio = Math.max(0, Math.min(1, ctx.p0DataIndex / (N - 2)));\n              return `rgb(${Math.round(sr + (er - sr) * ratio)},${Math.round(sg + (eg - sg) * ratio)},${Math.round(sb + (eb - sb) * ratio)})`;\n            },\n          },\n        },\n        {\n          data: [data[0]],\n          pointRadius: 8,\n          pointHoverRadius: 8,\n          pointBackgroundColor: cs,\n          pointBorderColor: t.pageBg,\n          pointBorderWidth: 2,\n          showLine: false,\n          label: \"t = 0\",\n        },\n        {\n          data: [data[data.length - 1]],\n          pointRadius: 8,\n          pointHoverRadius: 8,\n          pointBackgroundColor: ce,\n          pointBorderColor: t.pageBg,\n          pointBorderWidth: 2,\n          showLine: false,\n          label: \"t = T\",\n        },\n      ],\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      animation: false,\n      plugins: {\n        title: {\n          display: true,\n          text: subtitle,\n          color: t.ink,\n          font: { size: 16, weight: \"600\" },\n          padding: { bottom: 10 },\n        },\n        legend: { display: false },\n      },\n      scales: {\n        x: {\n          min: range[0],\n          max: range[1],\n          ticks: { color: t.inkSoft, font: { size: 13 }, maxTicksLimit: 7 },\n          grid: { color: t.grid },\n          title: { display: true, text: \"x(t)\", color: t.ink, font: { size: 14 } },\n          border: { display: false },\n        },\n        y: {\n          min: range[0],\n          max: range[1],\n          ticks: { color: t.inkSoft, font: { size: 13 }, maxTicksLimit: 7 },\n          grid: { color: t.grid },\n          title: { display: true, text: \"y(t)\", color: t.ink, font: { size: 14 } },\n          border: { display: false },\n        },\n      },\n      layout: { padding: 12 },\n    },\n  });\n}\n"}