{"spec_id":"streamline-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nstreamline-basic: Basic Streamline Plot\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\n\n\n# Prevent this script's directory from shadowing the pygal package\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not (p == script_dir or p == \"\")]\n\nimport pygal\nfrom pygal.style import Style\n\n\n# Theme 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# Okabe-Ito palette\nIMPRINT = (\n    \"#009E73\",  # Brand green - position 1\n    \"#C475FD\",  # Vermillion - position 2\n    \"#4467A3\",  # Blue - position 3\n    \"#BD8233\",  # Reddish purple - position 4\n)\n\n# Data - Multiple streamline patterns to showcase varied flow\n# Vortex pattern: u = -y, v = x (counterclockwise circulation)\n# We'll create streamlines at different starting points\n\nstreamlines = []\n\n# Vortex streamlines from 4 orbital radii\nradii = [0.7, 1.4, 2.1, 2.8]\npoints_per_radius = 5\n\nfor radius_idx, radius in enumerate(radii):\n    for angle_idx in range(points_per_radius):\n        angle = 2 * np.pi * angle_idx / points_per_radius\n        x0 = radius * np.cos(angle)\n        y0 = radius * np.sin(angle)\n\n        # Trace streamline from this starting point\n        points = [(x0, y0)]\n        x, y = x0, y0\n        dt = 0.02\n        max_steps = 400\n        bounds = 3.5\n\n        for _ in range(max_steps):\n            # Velocity field: vortex with radial decay\n            r = np.sqrt(x**2 + y**2)\n            factor = 1.0 / (1.0 + 0.1 * r)\n            u = -y * factor\n            v = x * factor\n            speed = np.sqrt(u**2 + v**2)\n\n            if speed < 0.001:\n                break\n\n            # Normalize and step\n            x_new = x + dt * u / speed\n            y_new = y + dt * v / speed\n\n            # Check bounds\n            if abs(x_new) > bounds or abs(y_new) > bounds:\n                break\n\n            x, y = x_new, y_new\n            points.append((x, y))\n\n        if len(points) > 10:\n            streamlines.append((points, radius_idx))\n\n# Group streamlines by orbital distance\nbinned_streamlines = {i: [] for i in range(4)}\nbin_labels = [\"Inner Orbit (r=0.7)\", \"Mid-Inner Orbit (r=1.4)\", \"Mid-Outer Orbit (r=2.1)\", \"Outer Orbit (r=2.8)\"]\n\nfor points, radius_idx in streamlines:\n    binned_streamlines[radius_idx].append(points)\n\n# Create custom style with theme-adaptive colors and proper sizing\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,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n)\n\n# Create chart\nchart = pygal.XY(\n    style=custom_style,\n    width=4800,\n    height=2700,\n    stroke=True,\n    stroke_style={\"width\": 3},\n    show_dots=False,\n    show_legend=True,\n    legend_at_bottom=True,\n    legend_at_bottom_columns=2,\n    title=\"streamline-basic · pygal · anyplot.ai\",\n    x_title=\"X Position\",\n    y_title=\"Y Position\",\n    show_x_guides=False,\n    show_y_guides=False,\n    range=(-4, 4),\n    xrange=(-4, 4),\n)\n\n# Add each orbital group as a series with Okabe-Ito colors\nfor bin_idx in range(4):\n    series_data = []\n    for streamline_points in binned_streamlines[bin_idx]:\n        # Add streamline points\n        for point in streamline_points:\n            series_data.append(point)\n        # Add None to separate streamlines\n        series_data.append(None)\n\n    if series_data:\n        chart.add(bin_labels[bin_idx], series_data)\n\n# Save outputs - theme-suffixed filenames\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}