{"spec_id":"smith-chart-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// smith-chart-basic: Smith Chart for RF/Impedance\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n//# anyplot-orientation: square\n// anyplot.ai\n// smith-chart-basic: Smith Chart for RF/Impedance\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-09-02\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ChartsText } from \"@mui/x-charts/ChartsText\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Antenna feed impedance sweep (in-memory, deterministic RLC model) ------\n// A series R-L-C feed model: radiation resistance rises gently with\n// frequency while the reactance swings through resonance — a textbook\n// Smith-chart trajectory for a monopole antenna matched to Z0.\nconst Z0 = 50; // ohms — reference impedance\nconst INDUCTANCE = 5e-9; // henries — series feed inductance\nconst CAPACITANCE = 0.4e-12; // farads — series feed capacitance\nconst FREQ_START_GHZ = 1;\nconst FREQ_END_GHZ = 6;\nconst FREQ_STEP_GHZ = 0.125;\n\nconst frequenciesGHz = [];\nfor (let f = FREQ_START_GHZ; f <= FREQ_END_GHZ + 1e-9; f += FREQ_STEP_GHZ) {\n  frequenciesGHz.push(Math.round(f * 1000) / 1000);\n}\n\nconst gammaPoints = frequenciesGHz.map((fGHz) => {\n  const omega = 2 * Math.PI * fGHz * 1e9;\n  const resistance = 35 + 3 * (fGHz - FREQ_START_GHZ);\n  const reactance = omega * INDUCTANCE - 1 / (omega * CAPACITANCE);\n  const zReal = resistance / Z0;\n  const zImag = reactance / Z0;\n  // gamma = (z_norm - 1) / (z_norm + 1), complex division done by hand\n  const denomReal = zReal + 1;\n  const denomImag = zImag;\n  const denomMagSq = denomReal * denomReal + denomImag * denomImag;\n  const numReal = zReal - 1;\n  const numImag = zImag;\n  return {\n    fGHz,\n    re: (numReal * denomReal + numImag * denomImag) / denomMagSq,\n    im: (numImag * denomReal - numReal * denomImag) / denomMagSq,\n  };\n});\nconst labeledFreqs = [1, 2, 3, 4, 5, 6];\n\n// --- Smith-chart grid geometry (unit circle in the Γ-plane) -----------------\nconst RESISTANCE_VALUES = [0.2, 0.5, 1, 2, 5];\nconst REACTANCE_VALUES = [0.2, 0.5, 1, 2, 5];\nconst GAMMA_MAX = 1.15;\nconst CIRCLE_STEPS = 120;\n\nconst circlePoints = (cx, cy, r, steps = CIRCLE_STEPS) =>\n  Array.from({ length: steps + 1 }, (_, i) => {\n    const theta = (i / steps) * 2 * Math.PI;\n    return [cx + r * Math.cos(theta), cy + r * Math.sin(theta)];\n  });\n\n// Every constant-reactance circle passes through the open-circuit point\n// (1, 0); only the portion that curves back into the unit disk belongs on\n// the chart. That portion's angular span depends on the circle's radius\n// (tiny for large |x|, most of the circle for small |x|), so it is found by\n// walking outward from (1, 0) until the circle re-crosses |Γ| = 1, rather\n// than assumed to be a fixed half-circle.\nconst reactanceArcPoints = (xVal, steps = 400) => {\n  const cx = 1;\n  const cy = 1 / xVal;\n  const r = Math.abs(1 / xVal);\n  const theta0 = Math.atan2(-cy, 0);\n  const pointAt = (theta) => [cx + r * Math.cos(theta), cy + r * Math.sin(theta)];\n  const norm2 = ([x, y]) => x * x + y * y;\n  const dTheta = (2 * Math.PI) / steps;\n  const direction = norm2(pointAt(theta0 + dTheta)) < norm2(pointAt(theta0 - dTheta)) ? 1 : -1;\n  const points = [pointAt(theta0)];\n  for (let i = 1; i <= steps; i++) {\n    const p = pointAt(theta0 + direction * dTheta * i);\n    if (norm2(p) > 1.0005) break;\n    points.push(p);\n  }\n  return points;\n};\n\nconst pathFromPoints = (points, toPx) =>\n  points\n    .map(([x, y], i) => {\n      const p = toPx(x, y);\n      return `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`;\n    })\n    .join(\" \");\n\n// --- Overlay: resistance circles, reactance arcs, matched-center marker -----\n// Community `@mui/x-charts/hooks` (useXScale/useYScale) map Γ-plane\n// coordinates to pixels so the grid stays aligned with the locus at any size.\nfunction SmithGrid() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  const toPx = (re, im) => ({ x: xScale(re), y: yScale(im) });\n\n  return (\n    <g>\n      <path d={pathFromPoints(circlePoints(0, 0, 1), toPx)} fill=\"none\" stroke={t.ink} strokeWidth={2.5} />\n      <line\n        x1={toPx(-1, 0).x}\n        y1={toPx(-1, 0).y}\n        x2={toPx(1, 0).x}\n        y2={toPx(1, 0).y}\n        stroke={t.inkSoft}\n        strokeWidth={1.5}\n      />\n      {RESISTANCE_VALUES.map((r) => (\n        <path\n          key={`r-${r}`}\n          d={pathFromPoints(circlePoints(r / (1 + r), 0, 1 / (1 + r)), toPx)}\n          fill=\"none\"\n          stroke={t.inkSoft}\n          strokeWidth={1}\n          opacity={0.55}\n        />\n      ))}\n      {REACTANCE_VALUES.flatMap((x) => [x, -x]).map((x) => (\n        <path\n          key={`x-${x}`}\n          d={pathFromPoints(reactanceArcPoints(x), toPx)}\n          fill=\"none\"\n          stroke={t.inkSoft}\n          strokeWidth={1}\n          opacity={0.55}\n        />\n      ))}\n      {RESISTANCE_VALUES.map((r) => {\n        const p = toPx((r - 1) / (1 + r), 0);\n        // r=1 sits exactly at the chart center, right next to the Z0 marker —\n        // give it extra clearance so the two labels don't cluster together.\n        const labelOffset = r === 1 ? 26 : 18;\n        return (\n          <ChartsText\n            key={`rl-${r}`}\n            x={p.x}\n            y={p.y + labelOffset}\n            text={String(r)}\n            style={{ fontSize: 13, fill: t.inkSoft, textAnchor: \"middle\" }}\n          />\n        );\n      })}\n      {REACTANCE_VALUES.flatMap((x) => [x, -x]).map((x) => {\n        // Label at the arc's outer end (where it re-crosses the boundary),\n        // nudged further out along the same radial direction from origin.\n        const arcPoints = reactanceArcPoints(x);\n        const [ax, ay] = arcPoints[arcPoints.length - 1];\n        const p = toPx(ax * 1.06, ay * 1.06);\n        return (\n          <ChartsText\n            key={`xl-${x}`}\n            x={p.x}\n            y={p.y}\n            text={`${x > 0 ? \"+j\" : \"−j\"}${Math.abs(x)}`}\n            style={{ fontSize: 13, fill: t.inkSoft, textAnchor: \"middle\", dominantBaseline: \"central\" }}\n          />\n        );\n      })}\n      <circle cx={toPx(0, 0).x} cy={toPx(0, 0).y} r={5} fill=\"none\" stroke={t.ink} strokeWidth={2} />\n      <ChartsText\n        x={toPx(0, 0).x}\n        y={toPx(0, 0).y - 26}\n        text=\"Z0\"\n        style={{ fontSize: 15, fill: t.ink, textAnchor: \"middle\", fontWeight: 500 }}\n      />\n    </g>\n  );\n}\n\n// --- Overlay: the swept impedance locus with frequency waypoints ------------\nfunction ImpedanceLocus() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  const toPx = (re, im) => ({ x: xScale(re), y: yScale(im) });\n  const brand = t.palette[0];\n\n  return (\n    <g>\n      <path\n        d={pathFromPoints(\n          gammaPoints.map((p) => [p.re, p.im]),\n          toPx,\n        )}\n        fill=\"none\"\n        stroke={brand}\n        strokeWidth={3.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      {gammaPoints\n        .filter((p) => labeledFreqs.includes(p.fGHz))\n        .map((p) => {\n          const px = toPx(p.re, p.im);\n          // Nudge the label outward along the radial direction from the chart\n          // center (same technique as the reactance-arc labels above) rather\n          // than a fixed pixel offset — points near |Γ|=1 (e.g. 1 GHz at\n          // |Γ|≈0.975) would otherwise sit on top of the boundary circle and\n          // converging grid arcs.\n          const origin = toPx(0, 0);\n          const dx = px.x - origin.x;\n          const dy = px.y - origin.y;\n          const dist = Math.hypot(dx, dy) || 1;\n          const ux = dx / dist;\n          const uy = dy / dist;\n          const LABEL_OFFSET_PX = 26;\n          const lx = px.x + ux * LABEL_OFFSET_PX;\n          const ly = px.y + uy * LABEL_OFFSET_PX;\n          return (\n            <g key={p.fGHz}>\n              <circle cx={px.x} cy={px.y} r={9} fill={brand} stroke={t.pageBg} strokeWidth={2.5} />\n              <ChartsText\n                x={lx}\n                y={ly}\n                text={`${p.fGHz} GHz`}\n                style={{\n                  fontSize: 14,\n                  fill: t.ink,\n                  fontWeight: 500,\n                  textAnchor: ux >= 0.15 ? \"start\" : ux <= -0.15 ? \"end\" : \"middle\",\n                  dominantBaseline: uy >= 0.15 ? \"hanging\" : uy <= -0.15 ? \"auto\" : \"central\",\n                }}\n              />\n            </g>\n          );\n        })}\n    </g>\n  );\n}\n\nconst TITLE = \"smith-chart-basic · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 70;\n\n// Left/right margin is derived so the drawing area is a perfect square —\n// required for the Γ-plane circles to render as true circles, not ellipses.\nconst BASE_MARGIN = 50;\nconst chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT;\nconst squareSide = chartHeight - 2 * BASE_MARGIN;\nconst sideMargin = (window.ANYPLOT_SIZE.width - squareSide) / 2;\nconst MARGIN = { top: BASE_MARGIN, bottom: BASE_MARGIN, left: sideMargin, right: sideMargin };\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  return (\n    <div\n      style={{\n        width: window.ANYPLOT_SIZE.width,\n        height: window.ANYPLOT_SIZE.height,\n        display: \"flex\",\n        flexDirection: \"column\",\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          fontSize: 22,\n          fontWeight: 500,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <ChartContainer\n        width={window.ANYPLOT_SIZE.width}\n        height={chartHeight}\n        margin={MARGIN}\n        series={[]}\n        skipAnimation\n        disableAxisListener\n        xAxis={[{ scaleType: \"linear\", min: -GAMMA_MAX, max: GAMMA_MAX }]}\n        yAxis={[{ scaleType: \"linear\", min: -GAMMA_MAX, max: GAMMA_MAX }]}\n      >\n        <SmithGrid />\n        <ImpedanceLocus />\n      </ChartContainer>\n    </div>\n  );\n}\n"}