{"spec_id":"scatter-complex-plane","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nscatter-complex-plane: Complex Plane Visualization (Argand Diagram)\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    arrow,\n    coord_fixed,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_label,\n    geom_path,\n    geom_point,\n    geom_segment,\n    geom_vline,\n    ggplot,\n    guide_legend,\n    guides,\n    labs,\n    scale_alpha_identity,\n    scale_color_manual,\n    scale_size_identity,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme-adaptive chrome tokens\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\"\n\n# Imprint categorical palette — 8 hues, hybrid-v3 sort\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\ncolor_roots = IMPRINT_PALETTE[0]  # brand green — 6th roots of unity\ncolor_arb = IMPRINT_PALETTE[1]  # lavender — arbitrary points\ncolor_sum = IMPRINT_PALETTE[2]  # blue — complex sum\n\n# Data — 6th roots of unity (evenly spaced at 60° intervals on unit circle)\nn_roots = 6\nangles_roots = np.array([2 * np.pi * k / n_roots for k in range(n_roots)])\nroots_real = np.cos(angles_roots)\nroots_imag = np.sin(angles_roots)\nroots_labels = [f\"ω{k}\" for k in range(n_roots)]\n\n# Arbitrary points spread across all four quadrants\narbitrary_real = np.array([1.5, -0.9, 0.4, -1.7])\narbitrary_imag = np.array([0.6, 1.1, -1.8, -0.8])\narbitrary_labels = [\"z₁\", \"z₂\", \"z₃\", \"z₄\"]\n\n# Complex addition: z₁ + z₂ — resultant vector only, no parallelogram construction lines\nsum_real = arbitrary_real[0] + arbitrary_real[1]  # 0.6\nsum_imag = arbitrary_imag[0] + arbitrary_imag[1]  # 1.7\n\nreal = np.concatenate([roots_real, arbitrary_real, [sum_real]])\nimag = np.concatenate([roots_imag, arbitrary_imag, [sum_imag]])\nlabels = roots_labels + arbitrary_labels + [\"z₁+z₂\"]\ncategory = [\"6th Root of Unity\"] * n_roots + [\"Arbitrary Point\"] * len(arbitrary_real) + [\"Sum (z₁+z₂)\"]\n\ndf_points = pd.DataFrame({\"real\": real, \"imag\": imag, \"label\": labels, \"category\": category})\n\n# Annotation text: point name + rectangular form (a+bi)\nannotations = []\nfor r, i, lbl in zip(real, imag, labels, strict=True):\n    sign = \"+\" if i >= 0 else \"−\"\n    annotations.append(f\"{lbl} = {r:.2f}{sign}{abs(i):.2f}i\")\ndf_points[\"annotation\"] = annotations\n\n# Vectors from origin to each point\ndf_vectors = pd.DataFrame(\n    {\"x\": [0.0] * len(real), \"y\": [0.0] * len(real), \"xend\": real, \"yend\": imag, \"category\": category}\n)\n\n# Unit circle reference\ntheta = np.linspace(0, 2 * np.pi, 300)\ndf_circle = pd.DataFrame({\"x\": np.cos(theta), \"y\": np.sin(theta)})\n\n# Radial label offsets — spread labels away from vectors to reduce crowding\nangles = np.arctan2(imag, real)\nlabel_radius = 0.46\nx_offsets = label_radius * np.cos(angles)\ny_offsets = label_radius * np.sin(angles)\nha_values = [\"left\" if np.cos(a) >= 0 else \"right\" for a in angles]\n\ndf_labels = pd.DataFrame({\"x\": real + x_offsets, \"y\": imag + y_offsets, \"annotation\": annotations, \"ha\": ha_values})\n\n# Visual hierarchy via point size (few data points → prominent markers)\ndf_points[\"pt_size\"] = [3.5] * n_roots + [4.0] * len(arbitrary_real) + [5.5]\ndf_points[\"pt_alpha\"] = [0.90] * n_roots + [0.95] * len(arbitrary_real) + [1.0]\n\ncolors = [color_roots, color_arb, color_sum]\n\nplot = (\n    ggplot()\n    # Reference axes through origin\n    + geom_hline(yintercept=0, color=INK_SOFT, size=0.4, linetype=\"solid\", alpha=0.4)\n    + geom_vline(xintercept=0, color=INK_SOFT, size=0.4, linetype=\"solid\", alpha=0.4)\n    # Unit circle — dashed reference\n    + geom_path(df_circle, aes(x=\"x\", y=\"y\"), color=INK_MUTED, linetype=\"dashed\", size=0.7, alpha=0.65)\n    + annotate(\"text\", x=0.62, y=0.80, label=\"∣z∣ = 1\", size=3.8, color=INK_MUTED, fontstyle=\"italic\", angle=50)\n    # Vectors from origin (no parallelogram construction lines)\n    + geom_segment(\n        df_vectors,\n        aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\", color=\"category\"),\n        size=0.55,\n        alpha=0.50,\n        arrow=arrow(length=0.09, type=\"closed\"),\n    )\n    # Points — main scatter layer with visual hierarchy\n    + geom_point(df_points, aes(x=\"real\", y=\"imag\", color=\"category\", size=\"pt_size\", alpha=\"pt_alpha\"))\n    + scale_size_identity()\n    + scale_alpha_identity()\n    # Halo ring on roots of unity for emphasis\n    + geom_point(\n        df_points[df_points[\"category\"] == \"6th Root of Unity\"],\n        aes(x=\"real\", y=\"imag\"),\n        size=7.5,\n        color=color_roots,\n        alpha=0.12,\n    )\n    # Annotation labels — split by horizontal alignment (plotnine limitation)\n    + geom_label(\n        df_labels[df_labels[\"ha\"] == \"left\"],\n        aes(x=\"x\", y=\"y\", label=\"annotation\"),\n        size=3.8,\n        color=INK,\n        fill=ELEVATED_BG,\n        alpha=0.88,\n        ha=\"left\",\n        fontweight=\"bold\",\n        label_padding=0.18,\n        label_size=0.2,\n        boxstyle=\"round,pad=0.2\",\n    )\n    + geom_label(\n        df_labels[df_labels[\"ha\"] == \"right\"],\n        aes(x=\"x\", y=\"y\", label=\"annotation\"),\n        size=3.8,\n        color=INK,\n        fill=ELEVATED_BG,\n        alpha=0.88,\n        ha=\"right\",\n        fontweight=\"bold\",\n        label_padding=0.18,\n        label_size=0.2,\n        boxstyle=\"round,pad=0.2\",\n    )\n    + labs(x=\"Re(z)\", y=\"Im(z)\", title=\"scatter-complex-plane · python · plotnine · anyplot.ai\", color=\"Category\")\n    + scale_x_continuous(limits=(-2.5, 2.5), breaks=[-2, -1, 0, 1, 2])\n    + scale_y_continuous(limits=(-2.5, 2.5), breaks=[-2, -1, 0, 1, 2])\n    + coord_fixed(ratio=1)\n    + scale_color_manual(values=colors, labels=[\"6th Root of Unity (∣z∣ = 1)\", \"Arbitrary Point\", \"Sum (z₁+z₂)\"])\n    + guides(color=guide_legend(override_aes={\"size\": 3.5, \"alpha\": 1.0}))\n    + theme_minimal()\n    + theme(\n        figure_size=(6, 6),\n        text=element_text(family=\"sans-serif\", size=7, color=INK),\n        plot_title=element_text(size=12, weight=\"bold\", color=INK, margin={\"b\": 10}),\n        axis_title_x=element_text(size=10, color=INK, margin={\"t\": 8}),\n        axis_title_y=element_text(size=10, color=INK, margin={\"r\": 8}),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        axis_ticks=element_line(color=INK_SOFT, size=0.3),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_title=element_text(size=9, weight=\"bold\", color=INK),\n        legend_position=\"bottom\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT, size=0.4),\n        legend_key=element_rect(fill=\"none\", color=\"none\"),\n        panel_background=element_rect(fill=PAGE_BG, color=\"none\"),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_line(color=INK, size=0.2, alpha=0.15),\n        panel_grid_minor=element_blank(),\n        panel_border=element_blank(),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=6, height=6, units=\"in\", verbose=False)\n"}