{"spec_id":"quiver-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nquiver-basic: Basic Quiver Plot\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 85/100 | Updated: 2026-07-24\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent this file from shadowing the installed plotly package\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _this_dir]\n\nimport numpy as np\nimport plotly.figure_factory as ff\nimport plotly.graph_objects as go\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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint palette — brand green is always the first (and here, only) series color\nBRAND = \"#009E73\"\n# Continuous magnitude is single-polarity (0 -> max), so the sequential Imprint cmap applies\nimprint_seq = [[0.0, \"#009E73\"], [1.0, \"#4467A3\"]]\n\n# Data - cyclonic wind vortex over a 4x4 km coastal grid (u = -y, v = x, m/s)\nnp.random.seed(42)\nx_grid = np.linspace(-2, 2, 13)\ny_grid = np.linspace(-2, 2, 13)\nX, Y = np.meshgrid(x_grid, y_grid)\n\nx = X.flatten()\ny = Y.flatten()\n# Cyclone-strength wind speeds (Beaufort ~5-30 m/s range) via a physical multiplier\nWIND_SPEED_SCALE = 10.0\nu = -Y.flatten() * WIND_SPEED_SCALE\nv = X.flatten() * WIND_SPEED_SCALE\n\n# True wind speed magnitude (0 at the eddy's calm centre, growing outward)\nmagnitude = np.sqrt(u**2 + v**2)\nmax_magnitude = magnitude.max()\nsafe_magnitude = np.where(magnitude == 0, 1e-6, magnitude)\n\n# Arrow length scales with relative magnitude (proportional encoding), with a\n# small floor so the calm-centre arrows stay visible instead of vanishing\nscale_factor = 0.32\nmin_length_frac = 0.18\nlength_frac = min_length_frac + (1 - min_length_frac) * (magnitude / max_magnitude)\nu_norm = (u / safe_magnitude) * length_frac * scale_factor\nv_norm = (v / safe_magnitude) * length_frac * scale_factor\n\n# Create quiver plot using figure_factory\nfig = ff.create_quiver(x, y, u_norm, v_norm, scale=1, arrow_scale=0.3, line={\"width\": 2, \"color\": BRAND})\n\n# Add scatter points at arrow bases for wind-speed coloring\nfig.add_trace(\n    go.Scatter(\n        x=x,\n        y=y,\n        mode=\"markers\",\n        marker={\n            \"size\": 6,\n            \"opacity\": 0.65,\n            \"color\": magnitude,\n            \"colorscale\": imprint_seq,\n            \"colorbar\": {\n                \"title\": {\"text\": \"Wind Speed (m/s)\", \"font\": {\"size\": 12, \"color\": INK}},\n                \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n                \"thickness\": 16,\n                \"outlinewidth\": 0,\n                \"len\": 0.8,\n            },\n            \"showscale\": True,\n        },\n        hovertemplate=\"x: %{x:.2f} km<br>y: %{y:.2f} km<br>speed: %{marker.color:.2f} m/s<extra></extra>\",\n    )\n)\n\n# Title fontsize scales linearly with length off the 67-char / 16px baseline\ntitle = \"Cyclonic Wind Vortex · quiver-basic · python · plotly · anyplot.ai\"\nratio = 67 / len(title) if len(title) > 67 else 1.0\ntitle_fontsize = max(11, round(16 * ratio))\n\n# Layout for 2400x2400 px (Step 0 canonical square canvas — the vortex is\n# radially symmetric with no preferred horizontal axis, and a square canvas\n# avoids the wasted lateral space a 1:1 aspect-locked field would leave in a\n# 16:9 frame)\nfig.update_layout(\n    autosize=False,\n    width=600,\n    height=600,\n    title={\"text\": title, \"font\": {\"size\": title_fontsize, \"color\": INK}, \"x\": 0.5},\n    xaxis={\n        \"title\": {\"text\": \"X Position (km)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [-2.3, 2.3],\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"zeroline\": True,\n        \"zerolinecolor\": INK_SOFT,\n        \"linecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Y Position (km)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [-2.3, 2.3],\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"zeroline\": True,\n        \"zerolinecolor\": INK_SOFT,\n        \"linecolor\": INK_SOFT,\n        \"scaleanchor\": \"x\",\n        \"scaleratio\": 1,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    showlegend=False,\n    margin={\"l\": 80, \"r\": 70, \"t\": 80, \"b\": 60},\n)\n\n# Save as PNG (2400x2400 px)\nfig.write_image(f\"plot-{THEME}.png\", width=600, height=600, scale=4)\n\n# Save as HTML for interactivity\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}