{"spec_id":"stem-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nstem-basic: Basic Stem Plot\nLibrary: pygal 3.1.3 | Python 3.13.14\nQuality: 84/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\n\n\n# Pop script dir so this file (pygal.py) doesn't shadow the installed pygal package\n_script_dir = sys.path.pop(0)\nimport pygal\nfrom pygal.style import Style\n\n\nsys.path.insert(0, _script_dir)\n\n# Theme tokens\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_GREEN = \"#009E73\"\n\n# Data - discrete impulse response (decaying oscillation)\nnp.random.seed(42)\nn_points = 30\nsample_index = np.arange(n_points)\namplitude = np.exp(-sample_index / 8) * np.sin(sample_index * 0.8) * 2 + np.random.randn(n_points) * 0.1\n\n# Custom style. Each stem is added as its own XY series (pygal's `None`\n# sentinel does not reliably break XY line strokes - it renders one\n# continuous connected path instead), so `colors` repeats the brand green\n# once per stem to keep every series the same color.\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_GREEN,) * n_points,\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    stroke_width=5,\n)\n\n# Chart\nchart = pygal.XY(\n    width=3200,\n    height=1800,\n    title=\"stem-basic · pygal · anyplot.ai\",\n    x_title=\"Sample Index (n)\",\n    y_title=\"Amplitude (a.u.)\",\n    style=custom_style,\n    show_dots=True,\n    dots_size=10,\n    stroke=True,\n    stroke_style={\"width\": 5},\n    show_x_guides=False,\n    show_y_guides=True,\n    show_legend=False,\n    margin=80,\n)\n\n# Each stem is its own series: baseline anchor (dot suppressed) -> peak\n# marker. Independent series (rather than one XY series broken by `None`)\n# guarantee no connector line is drawn between stems, and a plain peak node\n# (no per-point radius override) keeps every marker the same constant size.\nfor i in range(n_points):\n    xi = float(sample_index[i])\n    yi = float(amplitude[i])\n    chart.add(f\"n={i}\", [{\"value\": (xi, 0.0), \"node\": {\"r\": 0}}, {\"value\": (xi, yi), \"label\": f\"n={i}, a={yi:.3f}\"}])\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"}