{"spec_id":"lightcurve-transit","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nlightcurve-transit: Astronomical Light Curve\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\n# Theme tokens (Imprint palette — theme-adaptive chrome)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\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# Data — simulated exoplanet transit (phase-folded, Kepler-style)\nnp.random.seed(42)\n\nn_points = 200\nphase = np.sort(np.random.uniform(0.0, 1.0, n_points))\n\ntransit_center = 0.5\ntransit_duration = 0.08\ntransit_depth = 0.01\n\n# Limb-darkened transit model (Gaussian approximation)\nmodel_phase = np.linspace(0.0, 1.0, 500)\nsigma = transit_duration / 3.5\nmodel_flux_curve = 1.0 - transit_depth * np.exp(-0.5 * ((model_phase - transit_center) / sigma) ** 2)\n\nmodel_flux = np.interp(phase, model_phase, model_flux_curve)\nflux_err = np.random.uniform(0.0015, 0.003, n_points)\nflux = model_flux + np.random.normal(0, 1, n_points) * flux_err\n\nin_transit = np.abs(phase - transit_center) < 3 * sigma\n\n# Scatter data points split by transit status\nout_transit_points = []\nin_transit_points = []\nfor i in range(n_points):\n    pt = {\n        \"value\": (round(float(phase[i]), 5), round(float(flux[i]), 6)),\n        \"label\": f\"φ={phase[i]:.3f}  F={flux[i]:.4f}±{flux_err[i]:.4f}\",\n    }\n    if in_transit[i]:\n        in_transit_points.append(pt)\n    else:\n        out_transit_points.append(pt)\n\n# Error bar caps — sampled every 3rd point to reduce visual clutter (±1σ endpoint dots)\nerr_cap_points = []\nfor i in range(0, n_points, 3):\n    x = round(float(phase[i]), 5)\n    err_cap_points.append(\n        {\"value\": (x, round(float(flux[i] - flux_err[i]), 6)), \"label\": f\"φ={phase[i]:.3f}  σ={flux_err[i]:.4f}\"}\n    )\n    err_cap_points.append(\n        {\"value\": (x, round(float(flux[i] + flux_err[i]), 6)), \"label\": f\"φ={phase[i]:.3f}  σ={flux_err[i]:.4f}\"}\n    )\n\n# Dense model curve for smooth transit shape\nmodel_points = [\n    (round(float(model_phase[i]), 5), round(float(model_flux_curve[i]), 6)) for i in range(len(model_phase))\n]\n\n# Style — Imprint palette, canonical order; error caps use INK_SOFT (secondary anchor)\ntitle_str = \"lightcurve-transit · python · pygal · anyplot.ai\"\nn = len(title_str)\ndefault_title_fs = 66\ntitle_fs = max(44, round(default_title_fs * 67 / n)) if n > 67 else default_title_fs\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=(\"#009E73\", \"#C475FD\", \"#4467A3\", INK_SOFT),\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    stroke_width=2.5,\n)\n\n# Plot\nflux_min = float(np.min(flux)) - 0.003\nflux_max = float(np.max(flux)) + 0.003\n\nchart = pygal.XY(\n    style=custom_style,\n    width=3200,\n    height=1800,\n    title=title_str,\n    x_title=\"Orbital Phase\",\n    y_title=\"Relative Flux\",\n    show_x_guides=False,\n    show_y_guides=True,\n    dots_size=5,\n    range=(flux_min, flux_max),\n    xrange=(0.0, 1.0),\n    margin_right=60,\n    margin_left=60,\n    margin_top=40,\n    margin_bottom=80,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=2,\n    tooltip_border_radius=8,\n    x_value_formatter=lambda x: f\"{x:.2f}\",\n    y_value_formatter=lambda y: f\"{y:.4f}\",\n)\n\n# Out-of-transit observations — Imprint position 1 (#009E73)\nchart.add(\"Out-of-Transit\", out_transit_points, stroke=False, dots_size=5)\n\n# In-transit observations — Imprint position 2 (#C475FD), larger for emphasis\nchart.add(\"In-Transit (dip)\", in_transit_points, stroke=False, dots_size=8)\n\n# Best-fit transit model — Imprint position 3 (#4467A3), smooth line\nchart.add(\"Transit Model\", model_points, stroke=True, show_dots=False, stroke_width=5)\n\n# Measurement error bounds (±1σ) — INK_SOFT secondary; small dots, sampled for clarity\nchart.add(\"Measurement Error (±1σ)\", err_cap_points, stroke=False, dots_size=2)\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"}