{"spec_id":"scatter-complex-plane","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nscatter-complex-plane: Complex Plane Visualization (Argand Diagram)\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 83/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\n# Theme tokens (Imprint palette)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT_PALETTE = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# Data — 3rd roots of unity + sample complex numbers + vector sum\nnp.random.seed(42)\nroots_of_unity = [np.exp(2j * np.pi * k / 3) for k in range(3)]\nsample_pts = [2.5 + 1.0j, -1.8 + 1.5j, 1.0 - 2.0j, -0.5 - 1.5j]\nz1, z2 = sample_pts[0], sample_pts[1]\nz_sum = z1 + z2  # complex addition: parallelogram / vector sum law\n\n# Pre-compute labels inline (no helper function)\nroot_labels = [\n    (\n        f\"ω{k}={z.real:.1f}\"\n        if abs(z.imag) < 1e-10\n        else f\"ω{k}={z.imag:.1f}i\"\n        if abs(z.real) < 1e-10\n        else f\"ω{k}={z.real:.1f}{z.imag:+.1f}i\"\n    )\n    for k, z in enumerate(roots_of_unity)\n]\npt_names = [\"z₁\", \"z₂\", \"z₃\", \"z₄\"]\npt_labels = [\n    (\n        f\"{n}={z.real:.1f}\"\n        if abs(z.imag) < 1e-10\n        else f\"{n}={z.imag:.1f}i\"\n        if abs(z.real) < 1e-10\n        else f\"{n}={z.real:.1f}{z.imag:+.1f}i\"\n    )\n    for n, z in zip(pt_names, sample_pts, strict=False)\n]\nsum_label = f\"z₁+z₂={z_sum.real:.1f}{z_sum.imag:+.1f}i\"\n\n# Unit circle reference points\ntheta = np.linspace(0, 2 * np.pi, 180)\nunit_circle = [(float(np.cos(t)), float(np.sin(t))) for t in theta]\n\n# Title — 51 chars, under 67-char baseline so default fontsize applies\ntitle = \"scatter-complex-plane · python · pygal · anyplot.ai\"\ntitle_fs = max(44, round(66 * 67 / max(len(title), 67)))\n\n# Style\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT_PALETTE,\n    font_family=\"DejaVu Sans, Helvetica, Arial, sans-serif\",\n    title_font_family=\"DejaVu Sans, Helvetica, Arial, sans-serif\",\n    title_font_size=title_fs,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    value_label_font_size=36,\n    stroke_width=2.5,\n    opacity=0.94,\n    opacity_hover=1.0,\n)\n\n# Chart — square canvas ensures equal aspect ratio for the Argand diagram\nchart = pygal.XY(\n    width=2400,\n    height=2400,\n    style=custom_style,\n    title=title,\n    x_title=\"Real Axis\",\n    y_title=\"Imaginary Axis\",\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=3,\n    legend_box_size=24,\n    stroke=False,\n    dots_size=12,\n    show_x_guides=True,\n    show_y_guides=True,\n    truncate_legend=-1,\n    margin_bottom=140,\n    margin_left=100,\n    margin_right=80,\n    margin_top=100,\n    range=(-3.5, 3.5),\n    xrange=(-3.5, 3.5),\n    print_values=False,\n    print_labels=True,\n    js=[],\n)\n\n# Series 1: Roots of Unity (3rd) — Imprint #009E73\nroots_series = []\nfor i, z in enumerate(roots_of_unity):\n    roots_series += [\n        {\"value\": (0.0, 0.0), \"label\": \"\"},\n        {\"value\": (float(z.real), float(z.imag)), \"label\": root_labels[i]},\n        None,\n    ]\nchart.add(\n    \"Roots of Unity (3rd)\",\n    roots_series,\n    stroke=True,\n    show_dots=True,\n    dots_size=20,\n    stroke_style={\"width\": 5, \"linecap\": \"round\", \"opacity\": 0.88},\n)\n\n# Series 2: Sample Points — Imprint #C475FD\npt_series = []\nfor i, z in enumerate(sample_pts):\n    pt_series += [\n        {\"value\": (0.0, 0.0), \"label\": \"\"},\n        {\"value\": (float(z.real), float(z.imag)), \"label\": pt_labels[i]},\n        None,\n    ]\nchart.add(\n    \"Sample Points\",\n    pt_series,\n    stroke=True,\n    show_dots=True,\n    dots_size=16,\n    stroke_style={\"width\": 3.5, \"linecap\": \"round\", \"opacity\": 0.75},\n)\n\n# Series 3: Vector Sum z₁+z₂ — Imprint #4467A3 (dashed = result of addition)\nchart.add(\n    \"z₁+z₂ (Vector Sum)\",\n    [{\"value\": (0.0, 0.0), \"label\": \"\"}, {\"value\": (float(z_sum.real), float(z_sum.imag)), \"label\": sum_label}],\n    stroke=True,\n    show_dots=True,\n    dots_size=22,\n    stroke_style={\"width\": 6, \"dasharray\": \"10, 5\", \"linecap\": \"round\", \"opacity\": 0.92},\n)\n\n# Series 4: Unit Circle — Imprint #BD8233, subtle dashed reference\nchart.add(\n    \"Unit Circle\",\n    unit_circle,\n    stroke=True,\n    show_dots=False,\n    fill=False,\n    stroke_style={\"width\": 2.5, \"dasharray\": \"6, 6\", \"opacity\": 0.40},\n)\n\n# Series 5: Origin — Imprint #AE3030, drawn last to render on top of all vectors\nchart.add(\"Origin\", [{\"value\": (0.0, 0.0), \"label\": \"\"}], stroke=False, dots_size=20)\n\n# Save\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}