{"spec_id":"line-parametric","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nline-parametric: Parametric Curve Plot\nLibrary: pygal 3.1.3 | Python 3.13.14\nQuality: 82/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome tokens\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — canonical order\nIMPRINT_PALETTE = (\n    \"#009E73\",  # 1 brand green   — Lissajous start\n    \"#C475FD\",  # 2 lavender      — Lissajous 2nd quarter\n    \"#4467A3\",  # 3 blue          — Lissajous 3rd quarter\n    \"#BD8233\",  # 4 ochre         — Lissajous end\n    \"#AE3030\",  # 5 matte red     — Spiral start\n    \"#2ABCCD\",  # 6 cyan          — Spiral 2nd quarter\n    \"#954477\",  # 7 rose          — Spiral 3rd quarter\n    \"#99B314\",  # 8 lime          — Spiral end\n)\n\n# Data\nn_points = 800\nn_segs = 4\nseg_size = n_points // n_segs\n\n# Curve 1: Lissajous (3, 2) — x = sin(3t), y = sin(2t), t in [0, 2pi] — closed curve\nt_liss = np.linspace(0, 2 * np.pi, n_points)\nliss_x = np.sin(3 * t_liss)\nliss_y = np.sin(2 * t_liss)\n\n# Curve 2: Archimedean spiral — x = (t/2pi)*cos(t), y = (t/2pi)*sin(t), t in [0, 2pi]\n# Normalised so radius reaches 1.0 at t=2pi, fitting the [-1.2, 1.2] axis range\nt_spiral = np.linspace(0, 2 * np.pi, n_points)\nspiral_x = (t_spiral / (2 * np.pi)) * np.cos(t_spiral)\nspiral_y = (t_spiral / (2 * np.pi)) * np.sin(t_spiral)\n\nfont = \"DejaVu Sans, Helvetica, Arial, sans-serif\"\n\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=font,\n    title_font_family=font,\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    tooltip_font_size=36,\n    tooltip_font_family=font,\n    opacity=1.0,\n    stroke_opacity=1.0,\n    stroke_width=10,\n)\n\nchart = pygal.XY(\n    width=2400,\n    height=2400,\n    style=custom_style,\n    title=\"line-parametric · python · pygal · anyplot.ai\",\n    x_title=\"Horizontal Position  x(t)\",\n    y_title=\"Vertical Position  y(t)\",\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=2,\n    legend_box_size=24,\n    stroke=True,\n    show_dots=False,\n    show_x_guides=True,\n    show_y_guides=True,\n    x_value_formatter=lambda v: f\"{v:.1f}\",\n    value_formatter=lambda v: f\"{v:.1f}\",\n    js=[],\n    xrange=(-1.2, 1.2),\n    range=(-1.2, 1.2),\n    print_values=False,\n    min_scale=3,\n    margin_bottom=440,\n    margin_left=80,\n    margin_right=60,\n    margin_top=60,\n    truncate_legend=-1,\n)\n\n# Segment labels — quarter-period intervals show traversal direction via colour\nliss_labels = [\"Lissajous  0 to pi/2\", \"Lissajous  pi/2 to pi\", \"Lissajous  pi to 3pi/2\", \"Lissajous  3pi/2 to 2pi\"]\nspiral_labels = [\"Spiral  0 to pi/2\", \"Spiral  pi/2 to pi\", \"Spiral  pi to 3pi/2\", \"Spiral  3pi/2 to 2pi\"]\n\n# Lissajous (3,2) — solid lines; colour gradient encodes traversal direction\nfor k in range(n_segs):\n    start = k * seg_size\n    end = min((k + 1) * seg_size + 1, n_points)\n    seg_data = [{\"value\": (float(liss_x[i]), float(liss_y[i]))} for i in range(start, end)]\n    chart.add(\n        liss_labels[k], seg_data, stroke_style={\"width\": 10, \"linecap\": \"round\", \"linejoin\": \"round\"}, show_dots=False\n    )\n\n# Archimedean spiral — dashed lines provide non-colour family disambiguation\n# Colour gradient encodes outward growth from origin (red) to radius 1 (lime)\nfor k in range(n_segs):\n    start = k * seg_size\n    end = min((k + 1) * seg_size + 1, n_points)\n    seg_data = [{\"value\": (float(spiral_x[i]), float(spiral_y[i]))} for i in range(start, end)]\n    chart.add(\n        spiral_labels[k],\n        seg_data,\n        stroke_style={\"width\": 10, \"dasharray\": \"16, 8\", \"linecap\": \"round\", \"linejoin\": \"round\"},\n        show_dots=False,\n    )\n\n# Traversal markers — explicit start/end dots for both curves\n# Both curves originate at the origin; Lissajous closes at (0,0), spiral ends at (1, 0)\nchart.add(\"● Start  t = 0  (both curves)\", [{\"value\": (0.0, 0.0)}], show_dots=True)\nchart.add(\"◆ Spiral end  t = 2pi\", [{\"value\": (float(spiral_x[-1]), float(spiral_y[-1]))}], show_dots=True)\n\n# Save — PNG + interactive HTML, both theme-suffixed\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}