{"spec_id":"streamline-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nstreamline-basic: Basic Streamline Plot\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_fixed,\n    element_line,\n    element_rect,\n    element_text,\n    geom_path,\n    geom_point,\n    ggplot,\n    labs,\n    scale_color_cmap,\n    theme,\n    theme_minimal,\n)\nfrom scipy.integrate import solve_ivp\nfrom scipy.interpolate import RegularGridInterpolator\n\n\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\"\nBRAND = \"#009E73\"\n\nnp.random.seed(42)\n\nnx, ny = 40, 40\nx = np.linspace(-3, 3, nx)\ny = np.linspace(-3, 3, ny)\nX, Y = np.meshgrid(x, y)\n\nU = -Y\nV = X\n\nu_interp = RegularGridInterpolator((y, x), U, bounds_error=False, fill_value=0)\nv_interp = RegularGridInterpolator((y, x), V, bounds_error=False, fill_value=0)\n\nstreamlines_data = []\narrow_data = []\nstreamline_id = 0\n\nstart_points = []\n\nfor sx in np.linspace(-2.8, -1.5, 4):\n    for sy in np.linspace(-2.5, 2.5, 6):\n        start_points.append((sx, sy))\n\nfor r in [0.6, 1.3, 2.2]:\n    for angle in np.linspace(0, 2 * np.pi, 6, endpoint=False):\n        start_points.append((r * np.cos(angle), r * np.sin(angle)))\n\nfor x0, y0 in start_points:\n    try:\n        result = solve_ivp(\n            lambda t, pos: [u_interp([pos[1], pos[0]])[0], v_interp([pos[1], pos[0]])[0]],\n            [0, 4],\n            [x0, y0],\n            max_step=0.05,\n            dense_output=True,\n        )\n        if result.success and len(result.t) > 2:\n            t_eval = np.linspace(0, result.t[-1], 100)\n            trajectory = result.sol(t_eval)\n\n            for j in range(len(t_eval)):\n                px, py = trajectory[0, j], trajectory[1, j]\n                if -3 <= px <= 3 and -3 <= py <= 3:\n                    speed = np.sqrt(px**2 + py**2)\n                    streamlines_data.append({\"x\": px, \"y\": py, \"streamline\": streamline_id, \"order\": j, \"speed\": speed})\n\n            arrow_idx = int(len(t_eval) * 0.6)\n            if arrow_idx < len(t_eval):\n                ax, ay = trajectory[0, arrow_idx], trajectory[1, arrow_idx]\n                if -3 <= ax <= 3 and -3 <= ay <= 3:\n                    arrow_speed = np.sqrt(ax**2 + ay**2)\n                    arrow_data.append({\"x\": ax, \"y\": ay, \"speed\": arrow_speed})\n\n            streamline_id += 1\n    except Exception:\n        pass\n\ndf = pd.DataFrame(streamlines_data)\ndf_arrows = pd.DataFrame(arrow_data)\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    plot_title=element_text(size=24, color=INK),\n    legend_text=element_text(size=16, color=INK_SOFT),\n    legend_title=element_text(size=18, color=INK),\n    figure_size=(16, 9),\n)\n\nplot = (\n    ggplot(df, aes(x=\"x\", y=\"y\", group=\"streamline\", color=\"speed\"))\n    + geom_path(size=1.2, alpha=0.8)\n    + geom_point(\n        data=df_arrows,\n        mapping=aes(x=\"x\", y=\"y\", color=\"speed\"),\n        shape=\">\",\n        size=4,\n        inherit_aes=False,\n        show_legend=False,\n    )\n    + scale_color_cmap(cmap_name=\"viridis\", name=\"Flow Speed\")\n    + labs(x=\"X Position\", y=\"Y Position\", title=\"streamline-basic · plotnine · anyplot.ai\")\n    + coord_fixed(ratio=1)\n    + theme_minimal()\n    + anyplot_theme\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}