{"spec_id":"streamline-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nstreamline-basic: Basic Streamline Plot\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 86/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.patches import FancyArrowPatch\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\"\n\n# Set seed for reproducibility\nnp.random.seed(42)\n\n# Vortex flow field: u = -y, v = x (creates circular streamlines)\nstreamlines_data = []\narrow_data = []\nstreamline_id = 0\n\n# Starting points at different radii\nradii = [0.8, 1.2, 1.6, 2.0, 2.4, 2.8]\nn_per_radius_map = {0.8: 3, 1.2: 4, 1.6: 5, 2.0: 5, 2.4: 6, 2.8: 6}\ndt = 0.03\nmax_steps = 250\n\nfor r in radii:\n    n_per_radius = n_per_radius_map[r]\n    for i in range(n_per_radius):\n        angle = 2 * np.pi * i / n_per_radius + (r * 0.15)\n        x = r * np.cos(angle)\n        y = r * np.sin(angle)\n        streamline_points = []\n\n        # Trace streamline using Euler integration\n        for step in range(max_steps):\n            if abs(x) > 3.2 or abs(y) > 3.2:\n                break\n\n            # Vector field: circular vortex\n            u = -y\n            v = x\n            speed = np.sqrt(u**2 + v**2)\n\n            if speed < 1e-6:\n                break\n\n            vel_mag = np.sqrt(x**2 + y**2)\n            streamlines_data.append(\n                {\n                    \"x\": float(x),\n                    \"y\": float(y),\n                    \"streamline_id\": streamline_id,\n                    \"order\": step,\n                    \"velocity\": float(vel_mag),\n                }\n            )\n            streamline_points.append((x, y, u, v, vel_mag))\n\n            x = x + dt * u / speed\n            y = y + dt * v / speed\n\n        # Store arrow position at midpoint\n        if len(streamline_points) > 20:\n            mid_idx = len(streamline_points) // 2\n            px, py, pu, pv, pvel = streamline_points[mid_idx]\n            arrow_data.append({\"x\": px, \"y\": py, \"u\": pu, \"v\": pv, \"velocity\": pvel})\n\n        streamline_id += 1\n\n# Create DataFrames\ndf = pd.DataFrame(streamlines_data)\narrows_df = pd.DataFrame(arrow_data)\n\n# Compute average velocity per streamline for color encoding\navg_velocity = df.groupby(\"streamline_id\")[\"velocity\"].mean().reset_index()\navg_velocity.columns = [\"streamline_id\", \"avg_velocity\"]\ndf = df.merge(avg_velocity, on=\"streamline_id\")\n\n# Configure seaborn with theme-adaptive colors\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n    },\n)\n\n# Create square figure for equal aspect ratio\nfig, ax = plt.subplots(figsize=(12, 12), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Use viridis colormap for continuous velocity data\npalette = sns.color_palette(\"viridis\", as_cmap=True)\nnorm = plt.Normalize(df[\"avg_velocity\"].min(), df[\"avg_velocity\"].max())\n\n# Plot streamlines with continuous color encoding\nsns.lineplot(\n    data=df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"avg_velocity\",\n    units=\"streamline_id\",\n    estimator=None,\n    sort=False,\n    linewidth=2.5,\n    alpha=0.85,\n    palette=\"viridis\",\n    legend=False,\n    ax=ax,\n)\n\n# Add arrowheads to show flow direction\ncmap = plt.cm.viridis\nfor _, arrow in arrows_df.iterrows():\n    px, py = arrow[\"x\"], arrow[\"y\"]\n    pu, pv = arrow[\"u\"], arrow[\"v\"]\n    speed = np.sqrt(pu**2 + pv**2)\n    dx = 0.15 * pu / speed\n    dy = 0.15 * pv / speed\n    color = cmap(norm(arrow[\"velocity\"]))\n    arrow_patch = FancyArrowPatch(\n        (px - dx / 2, py - dy / 2),\n        (px + dx / 2, py + dy / 2),\n        arrowstyle=\"->,head_width=4,head_length=4\",\n        color=color,\n        linewidth=2,\n        mutation_scale=1,\n        zorder=10,\n    )\n    ax.add_patch(arrow_patch)\n\n# Add colorbar\nsm = plt.cm.ScalarMappable(cmap=\"viridis\", norm=norm)\nsm.set_array([])\ncbar = fig.colorbar(sm, ax=ax, shrink=0.8, aspect=20)\ncbar.set_label(\"Flow Speed (m/s)\", fontsize=20, color=INK)\ncbar.ax.tick_params(labelsize=16, colors=INK_SOFT)\n\n# Style axes\nax.set_xlabel(\"X Position (m)\", fontsize=20, color=INK)\nax.set_ylabel(\"Y Position (m)\", fontsize=20, color=INK)\nax.set_title(\"streamline-basic · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\nax.set_aspect(\"equal\")\nax.set_xlim(-3.5, 3.5)\nax.set_ylim(-3.5, 3.5)\n\n# Remove spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Subtle grid\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}