{"spec_id":"scatter-complex-plane","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nscatter-complex-plane: Complex Plane Visualization (Argand Diagram)\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot.export import ggsave as export_ggsave\n\n\nLetsPlot.setup_html()\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Imprint palette — theme-independent, canonical order\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Theme-adaptive chrome tokens\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\"\nRULE = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Unit circle subtle tint using Imprint brand green\nCIRCLE_ALPHA = 0.08 if THEME == \"light\" else 0.12\n\n# Data — 5th roots of unity + arbitrary points + complex sum\nnp.random.seed(42)\n\nn_roots = 5\nroots_angles = np.array([2 * np.pi * k / n_roots for k in range(n_roots)])\nroots_real = np.cos(roots_angles)\nroots_imag = np.sin(roots_angles)\nroots_labels = [f\"ω{k}\" for k in range(n_roots)]\n\narbitrary_real = np.array([1.8, -2.0, 0.5, -0.4, 2.3, -1.6])\narbitrary_imag = np.array([1.3, -1.4, 2.1, -1.9, -0.5, 0.7])\narbitrary_labels = [f\"z{i + 1}\" for i in range(len(arbitrary_real))]\n\nsum_real = np.array([roots_real[1] + roots_real[2]])\nsum_imag = np.array([roots_imag[1] + roots_imag[2]])\nsum_labels = [\"ω1+ω2\"]\n\nall_real = np.concatenate([roots_real, arbitrary_real, sum_real])\nall_imag = np.concatenate([roots_imag, arbitrary_imag, sum_imag])\nall_labels = roots_labels + arbitrary_labels + sum_labels\nall_category = [\"5th Root of Unity\"] * n_roots + [\"Arbitrary\"] * len(arbitrary_real) + [\"Sum\"]\n\nall_mag = np.sqrt(all_real**2 + all_imag**2)\nall_angle_deg = np.degrees(np.arctan2(all_imag, all_real))\n\nrect_labels = []\nfor r, i in zip(all_real, all_imag, strict=True):\n    sign = \"+\" if i >= 0 else \"−\"\n    rect_labels.append(f\"{r:.2f}{sign}{abs(i):.2f}i\")\n\npolar_labels = []\nfor mag, ang in zip(all_mag, all_angle_deg, strict=True):\n    polar_labels.append(f\"r={mag:.2f}, θ={ang:.0f}°\")\n\ndf = pd.DataFrame(\n    {\n        \"real\": all_real,\n        \"imaginary\": all_imag,\n        \"label\": all_labels,\n        \"category\": all_category,\n        \"rect_form\": rect_labels,\n        \"polar_form\": polar_labels,\n        \"magnitude\": all_mag,\n        \"angle_deg\": all_angle_deg,\n    }\n)\n\n# Radial label placement with iterative repulsion to reduce overlap\nangles = np.arctan2(all_imag, all_real)\nbase_offset = 0.5\nlabel_x = all_real + base_offset * np.cos(angles)\nlabel_y = all_imag + base_offset * np.sin(angles) + 0.2\n\nmin_sep = 0.9\nfor _ in range(20):\n    for i in range(len(label_x)):\n        for j in range(i + 1, len(label_x)):\n            dx = label_x[i] - label_x[j]\n            dy = label_y[i] - label_y[j]\n            dist = np.sqrt(dx**2 + dy**2)\n            if dist < min_sep:\n                push = 0.15 * (min_sep - dist) / max(dist, 0.01)\n                label_x[i] += dx * push\n                label_y[i] += dy * push\n                label_x[j] -= dx * push\n                label_y[j] -= dy * push\n\ndf[\"label_x\"] = label_x\ndf[\"label_y\"] = label_y\n\n# Vectors from origin to each point\narrows_df = pd.DataFrame(\n    {\n        \"x_start\": np.zeros(len(all_real)),\n        \"y_start\": np.zeros(len(all_real)),\n        \"x_end\": all_real,\n        \"y_end\": all_imag,\n        \"category\": all_category,\n    }\n)\n\n# Unit circle path\ntheta = np.linspace(0, 2 * np.pi, 200)\ncircle_df = pd.DataFrame({\"x\": np.cos(theta), \"y\": np.sin(theta)})\n\n# 3 categories use Imprint positions 1–3\ncolors = IMPRINT_PALETTE[:3]\n\n# Tooltips for HTML interactivity — lets-plot distinctive feature\npoint_tooltips = (\n    layer_tooltips()\n    .title(\"@label\")\n    .line(\"Category|@category\")\n    .line(\"Rectangular|@rect_form\")\n    .line(\"Polar|@polar_form\")\n    .format(\"magnitude\", \".3f\")\n    .format(\"angle_deg\", \".1f\")\n)\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=RULE, size=0.25),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(size=12, color=INK),\n    axis_text=element_text(size=10, color=INK_SOFT),\n    axis_line=element_blank(),\n    panel_border=element_blank(),\n    plot_title=element_text(size=14, color=INK, face=\"bold\"),\n    plot_subtitle=element_text(size=10, color=INK_SOFT, face=\"italic\"),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(size=10, color=INK_SOFT),\n    legend_title=element_text(size=11, color=INK),\n    plot_margin=[20, 35, 15, 15],\n)\n\nplot = (\n    ggplot()\n    # Unit circle filled region (subtle Imprint green tint)\n    + geom_polygon(\n        data=circle_df,\n        mapping=aes(x=\"x\", y=\"y\"),\n        fill=IMPRINT_PALETTE[0],\n        alpha=CIRCLE_ALPHA,\n        color=\"rgba(0,0,0,0)\",\n    )\n    # Dashed unit circle reference\n    + geom_path(\n        data=circle_df,\n        mapping=aes(x=\"x\", y=\"y\"),\n        color=INK_SOFT,\n        size=0.8,\n        linetype=\"dashed\",\n    )\n    # Real and imaginary axes through origin\n    + geom_hline(yintercept=0, color=INK_SOFT, size=0.4)\n    + geom_vline(xintercept=0, color=INK_SOFT, size=0.4)\n    # Vectors from origin (magnitude and phase visualization)\n    + geom_segment(\n        data=arrows_df,\n        mapping=aes(\n            x=\"x_start\", y=\"y_start\", xend=\"x_end\", yend=\"y_end\", color=\"category\"\n        ),\n        size=1.0,\n        alpha=0.6,\n        arrow=arrow(length=10, type=\"open\"),\n    )\n    # Points with interactive tooltips\n    + geom_point(\n        data=df,\n        mapping=aes(x=\"real\", y=\"imaginary\", color=\"category\"),\n        size=5,\n        alpha=0.95,\n        shape=21,\n        fill=PAGE_BG,\n        stroke=2.0,\n        tooltips=point_tooltips,\n    )\n    # Bold name labels positioned radially outward\n    + geom_text(\n        data=df,\n        mapping=aes(x=\"label_x\", y=\"label_y\", label=\"label\", color=\"category\"),\n        size=4,\n        fontface=\"bold\",\n    )\n    # Rectangular form (a+bi) below name\n    + geom_text(\n        data=df,\n        mapping=aes(x=\"label_x\", y=\"label_y\", label=\"rect_form\"),\n        size=3.5,\n        color=INK_SOFT,\n        nudge_y=-0.26,\n    )\n    + scale_color_manual(values=colors)\n    + coord_fixed()\n    + labs(\n        x=\"Real Part\",\n        y=\"Imaginary Part\",\n        title=\"scatter-complex-plane · python · letsplot · anyplot.ai\",\n        subtitle=\"5th roots of unity, arbitrary points, and complex addition on the Argand diagram\",\n        color=\"Category\",\n    )\n    + scale_x_continuous(breaks=[-3, -2, -1, 0, 1, 2, 3], expand=[0.12, 0.12])\n    + scale_y_continuous(breaks=[-3, -2, -1, 0, 1, 2, 3], expand=[0.12, 0.12])\n    # Square canvas: 600×600 × scale=4 → 2400×2400 px (equal-aspect complex plane)\n    + ggsize(600, 600)\n    + anyplot_theme\n)\n\n# Save PNG (scale=4 → 2400×2400 px) and interactive HTML to current directory\nexport_ggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=4)\nexport_ggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}