{"spec_id":"streamline-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nstreamline-basic: Basic Streamline Plot\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_path,\n    ggplot,\n    ggsize,\n    labs,\n    scale_color_gradient,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\nfrom scipy.integrate import solve_ivp\n\n\nLetsPlot.setup_html()\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\"\n\n# Data - Create a vortex flow field: u = -y, v = x (circular streamlines)\nnp.random.seed(42)\n\nx_min, x_max = -3, 3\ny_min, y_max = -3, 3\n\n# Seed points for streamlines - distributed radially for good coverage\nradii = np.linspace(0.3, 2.8, 8)\nseed_points = [[r, 0.0] for r in radii]\n\n# Integrate streamlines forward\nstreamline_data = []\nstreamline_id = 0\n\nfor seed in seed_points:\n    t_span = [0, 2 * np.pi]\n    t_eval = np.linspace(0, 2 * np.pi, 100)\n\n    # Inline velocity field calculation: rotation field u=-y, v=x\n    def velocity_field(t, point):\n        x, y = point\n        return [-y, x]\n\n    try:\n        sol = solve_ivp(velocity_field, t_span, seed, t_eval=t_eval, method=\"RK45\", dense_output=True, max_step=0.1)\n\n        if sol.success:\n            xs = sol.y[0]\n            ys = sol.y[1]\n\n            mask = (xs >= x_min) & (xs <= x_max) & (ys >= y_min) & (ys <= y_max)\n\n            if np.any(mask):\n                xs_clipped = xs[mask]\n                ys_clipped = ys[mask]\n                magnitudes = np.sqrt(xs_clipped**2 + ys_clipped**2)\n\n                for i in range(len(xs_clipped)):\n                    streamline_data.append(\n                        {\n                            \"x\": xs_clipped[i],\n                            \"y\": ys_clipped[i],\n                            \"magnitude\": magnitudes[i],\n                            \"streamline\": streamline_id,\n                        }\n                    )\n                streamline_id += 1\n    except Exception:\n        continue\n\ndf = pd.DataFrame(streamline_data)\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"x\", y=\"y\", group=\"streamline\", color=\"magnitude\"))\n    + geom_path(size=1.5, alpha=0.85)\n    + scale_color_gradient(low=\"#306998\", high=\"#FFD43B\", name=\"Field Strength\")\n    + labs(x=\"X Position\", y=\"Y Position\", title=\"streamline-basic · letsplot · anyplot.ai\")\n    + ggsize(1600, 900)\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major=element_line(color=INK_SOFT, size=0.3),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_title=element_text(size=20, color=INK),\n        plot_title=element_text(size=24, color=INK),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=14, color=INK_SOFT),\n    )\n)\n\n# Save PNG (scale 3x to get 4800 x 2700 px)\nggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Save HTML for interactive version\nggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}