{"spec_id":"scatter-complex-plane","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nscatter-complex-plane: Complex Plane Visualization (Argand Diagram)\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens — Imprint palette chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint categorical palette — positions 1 and 2\nC1 = \"#009E73\"  # green  — 8th roots of unity\nC2 = \"#C475FD\"  # purple — inward geometric spiral\n\n# Data: 8th roots of unity  ω = e^(2πi/8)\nn_roots = 8\nroots = [np.exp(2j * np.pi * k / n_roots) for k in range(n_roots)]\nroot_labels_display = [\"1\", \"ω\", \"i\", \"ω³\", \"−1\", \"ω⁵\", \"−i\", \"ω⁷\"]\nroot_labels_hover = [f\"ω^{k}\" for k in range(n_roots)]\n\n# Data: geometric spiral — repeated multiplication by z₀ = 0.88·e^(iπ/6)\n# Each step rotates 30° and contracts by factor 0.88; after 12 steps |z₀|¹² ≈ 0.20\nz0 = 0.88 * np.exp(1j * np.pi / 6)\nn_spiral = 12\nspiral_pts = [z0**k for k in range(n_spiral)]\n\n\n# Helper: text anchor relative to angle on unit circle\ndef root_anchor(k, n):\n    deg = 360 * k / n\n    if deg < 22.5 or deg >= 337.5:\n        return \"left\", 16, 0\n    elif deg < 67.5:\n        return \"left\", 10, 14\n    elif deg < 112.5:\n        return \"center\", 0, 18\n    elif deg < 157.5:\n        return \"right\", -10, 14\n    elif deg < 202.5:\n        return \"right\", -16, 0\n    elif deg < 247.5:\n        return \"right\", -10, -14\n    elif deg < 292.5:\n        return \"center\", 0, -18\n    else:\n        return \"left\", 10, -14\n\n\n# Plot\nfig = go.Figure()\n\n# Unit circle reference (dashed)\ntheta_c = np.linspace(0, 2 * np.pi, 300)\nfig.add_trace(\n    go.Scatter(\n        x=np.cos(theta_c).tolist(),\n        y=np.sin(theta_c).tolist(),\n        mode=\"lines\",\n        line={\"color\": INK_MUTED, \"width\": 2, \"dash\": \"dash\"},\n        name=\"Unit circle |z| = 1\",\n        hoverinfo=\"skip\",\n        showlegend=True,\n    )\n)\n\n# Spiral connecting path (dotted)\nfig.add_trace(\n    go.Scatter(\n        x=[z.real for z in spiral_pts],\n        y=[z.imag for z in spiral_pts],\n        mode=\"lines\",\n        line={\"color\": C2, \"width\": 1.5, \"dash\": \"dot\"},\n        hoverinfo=\"skip\",\n        showlegend=False,\n    )\n)\n\n# Vector arrows from origin to each 8th root\nfor pt in roots:\n    fig.add_annotation(\n        x=pt.real,\n        y=pt.imag,\n        ax=0,\n        ay=0,\n        xref=\"x\",\n        yref=\"y\",\n        axref=\"x\",\n        ayref=\"y\",\n        showarrow=True,\n        arrowhead=3,\n        arrowsize=1.2,\n        arrowwidth=2.2,\n        arrowcolor=C1,\n        opacity=0.65,\n    )\n\n# Root label annotations\nfor k, (pt, label) in enumerate(zip(roots, root_labels_display, strict=True)):\n    xa, xs, ys = root_anchor(k, n_roots)\n    ya = \"middle\"\n    if ys > 0:\n        ya = \"bottom\"\n    elif ys < 0:\n        ya = \"top\"\n    fig.add_annotation(\n        x=pt.real,\n        y=pt.imag,\n        text=f\"<b>{label}</b>\",\n        showarrow=False,\n        font={\"size\": 14, \"color\": C1},\n        xanchor=xa,\n        yanchor=ya,\n        xshift=xs,\n        yshift=ys,\n    )\n\n# Scatter trace: 8th roots of unity\nfig.add_trace(\n    go.Scatter(\n        x=[z.real for z in roots],\n        y=[z.imag for z in roots],\n        mode=\"markers\",\n        marker={\"size\": 16, \"color\": C1, \"line\": {\"color\": PAGE_BG, \"width\": 2.5}, \"symbol\": \"circle\"},\n        name=\"8th roots of unity\",\n        customdata=[[root_labels_hover[k], abs(roots[k]), np.degrees(np.angle(roots[k]))] for k in range(n_roots)],\n        hovertemplate=(\n            \"<b>%{customdata[0]}</b><br>\"\n            \"Re: %{x:.4f}<br>Im: %{y:.4f}<br>\"\n            \"|z| = %{customdata[1]:.4f}<br>\"\n            \"arg(z) = %{customdata[2]:.1f}°\"\n            \"<extra>8th roots of unity</extra>\"\n        ),\n    )\n)\n\n# Scatter trace: geometric spiral (marker size decreases to show convergence)\nspiral_sizes = [max(7, round(16 - k * 0.75)) for k in range(n_spiral)]\nfig.add_trace(\n    go.Scatter(\n        x=[z.real for z in spiral_pts],\n        y=[z.imag for z in spiral_pts],\n        mode=\"markers\",\n        marker={\"size\": spiral_sizes, \"color\": C2, \"line\": {\"color\": PAGE_BG, \"width\": 2}, \"symbol\": \"diamond\"},\n        name=\"z₀ᵏ, z₀ = 0.88·e^(iπ/6)\",\n        customdata=[[k, abs(spiral_pts[k]), np.degrees(np.angle(spiral_pts[k]))] for k in range(n_spiral)],\n        hovertemplate=(\n            \"<b>z₀^%{customdata[0]}</b><br>\"\n            \"Re: %{x:.4f}<br>Im: %{y:.4f}<br>\"\n            \"|z₀ᵏ| = %{customdata[1]:.4f}<br>\"\n            \"arg = %{customdata[2]:.1f}°\"\n            \"<extra>Geometric spiral</extra>\"\n        ),\n    )\n)\n\n# \"r = 1\" label at 120° on unit circle — clear of all data\nfig.add_annotation(\n    x=np.cos(np.radians(120)),\n    y=np.sin(np.radians(120)),\n    text=\"<i>r</i> = 1\",\n    showarrow=False,\n    font={\"size\": 12, \"color\": INK_MUTED},\n    xshift=-16,\n    yshift=12,\n)\n\n# Title — compute font size from character count\ntitle_str = \"scatter-complex-plane · python · plotly · anyplot.ai\"\nn_chars = len(title_str)\ndefault_fs = 16\ntitle_fs = max(11, round(default_fs * 67 / n_chars)) if n_chars > 67 else default_fs\n\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": (\n            f\"{title_str}<br>\"\n            f\"<sup style='color:{INK_MUTED}; font-weight:normal'>\"\n            \"Argand diagram — 8th roots of unity &amp; inward geometric spiral\"\n            \"</sup>\"\n        ),\n        \"font\": {\"size\": title_fs, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Re(z)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"zeroline\": True,\n        \"zerolinewidth\": 2,\n        \"zerolinecolor\": INK_SOFT,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"showgrid\": True,\n        \"dtick\": 0.5,\n        \"range\": [-1.45, 1.45],\n    },\n    yaxis={\n        \"title\": {\"text\": \"Im(z)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"zeroline\": True,\n        \"zerolinewidth\": 2,\n        \"zerolinecolor\": INK_SOFT,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"showgrid\": True,\n        \"scaleanchor\": \"x\",\n        \"scaleratio\": 1,\n        \"dtick\": 0.5,\n        \"range\": [-1.45, 1.45],\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    legend={\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n        \"x\": 0.01,\n        \"y\": 0.99,\n        \"xanchor\": \"left\",\n        \"yanchor\": \"top\",\n    },\n    margin={\"l\": 80, \"r\": 40, \"t\": 80, \"b\": 60},\n)\n\n# Save — square canvas: 600×600 × scale=4 → 2400×2400 px\nfig.write_image(f\"plot-{THEME}.png\", width=600, height=600, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}