|
| 1 | +""" pyplots.ai |
| 2 | +streamline-basic: Basic Streamline Plot |
| 3 | +Library: pygal 3.1.0 | Python 3.13.11 |
| 4 | +Quality: 77/100 | Created: 2025-12-31 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pygal |
| 9 | +from pygal.style import Style |
| 10 | + |
| 11 | + |
| 12 | +# Data - Vortex flow field (circular streamlines) |
| 13 | +# u = -y, v = x creates counterclockwise circular flow |
| 14 | +np.random.seed(42) |
| 15 | + |
| 16 | +# Trace streamlines inline - KISS structure (no helper functions) |
| 17 | +# Starting points distributed at different radii to show flow structure |
| 18 | +streamlines = [] |
| 19 | + |
| 20 | +# Use 4 radii to show different orbital distances |
| 21 | +radii = [0.8, 1.3, 1.8, 2.3] |
| 22 | +points_per_radius = 5 |
| 23 | + |
| 24 | +for radius in radii: |
| 25 | + for angle_idx in range(points_per_radius): |
| 26 | + angle = 2 * np.pi * angle_idx / points_per_radius |
| 27 | + x0 = radius * np.cos(angle) |
| 28 | + y0 = radius * np.sin(angle) |
| 29 | + |
| 30 | + # Trace streamline from this starting point |
| 31 | + points = [(x0, y0)] |
| 32 | + x, y = x0, y0 |
| 33 | + dt = 0.02 # Smaller step for smoother curves |
| 34 | + max_steps = 400 # More steps for longer, smoother curves |
| 35 | + bounds = 3.0 |
| 36 | + |
| 37 | + for _ in range(max_steps): |
| 38 | + # Velocity field: vortex with radial decay |
| 39 | + r = np.sqrt(x**2 + y**2) |
| 40 | + factor = 1.0 / (1.0 + 0.1 * r) |
| 41 | + u = -y * factor |
| 42 | + v = x * factor |
| 43 | + speed = np.sqrt(u**2 + v**2) |
| 44 | + |
| 45 | + if speed < 0.001: # Stagnation point |
| 46 | + break |
| 47 | + |
| 48 | + # Normalize and step |
| 49 | + x_new = x + dt * u / speed |
| 50 | + y_new = y + dt * v / speed |
| 51 | + |
| 52 | + # Check bounds |
| 53 | + if abs(x_new) > bounds or abs(y_new) > bounds: |
| 54 | + break |
| 55 | + |
| 56 | + x, y = x_new, y_new |
| 57 | + points.append((x, y)) |
| 58 | + |
| 59 | + if len(points) > 10: # Only keep meaningful streamlines |
| 60 | + streamlines.append((points, radius)) |
| 61 | + |
| 62 | +# Group streamlines by radial distance for coloring |
| 63 | +# 4 distinct high-contrast colors for different radii |
| 64 | +# Labels describe orbit radius, not "speed" - for vortex flow, all orbits have same |
| 65 | +# angular velocity so inner orbits have lower linear speed |
| 66 | +bin_colors = ["#d62728", "#2ca02c", "#1f77b4", "#9467bd"] |
| 67 | +bin_labels = ["Inner Orbit (r=0.8)", "Mid-Inner Orbit (r=1.3)", "Mid-Outer Orbit (r=1.8)", "Outer Orbit (r=2.3)"] |
| 68 | + |
| 69 | +binned_streamlines = {i: [] for i in range(4)} |
| 70 | +for points, radius in streamlines: |
| 71 | + bin_idx = radii.index(radius) |
| 72 | + binned_streamlines[bin_idx].append(points) |
| 73 | + |
| 74 | +# Custom style with larger fonts for readability |
| 75 | +custom_style = Style( |
| 76 | + background="white", |
| 77 | + plot_background="white", |
| 78 | + foreground="#333333", |
| 79 | + foreground_strong="#333333", |
| 80 | + foreground_subtle="#555555", |
| 81 | + colors=tuple(bin_colors), |
| 82 | + title_font_size=72, |
| 83 | + label_font_size=48, |
| 84 | + major_label_font_size=40, |
| 85 | + legend_font_size=48, |
| 86 | + value_font_size=32, |
| 87 | + guide_stroke_color="#cccccc", |
| 88 | +) |
| 89 | + |
| 90 | +# Create chart |
| 91 | +chart = pygal.XY( |
| 92 | + style=custom_style, |
| 93 | + width=4800, |
| 94 | + height=2700, |
| 95 | + stroke=True, |
| 96 | + stroke_style={"width": 10}, |
| 97 | + show_dots=False, |
| 98 | + show_legend=True, |
| 99 | + legend_at_bottom=True, |
| 100 | + legend_at_bottom_columns=4, |
| 101 | + title="streamline-basic · pygal · pyplots.ai", |
| 102 | + x_title="X Position", |
| 103 | + y_title="Y Position", |
| 104 | + show_x_guides=True, |
| 105 | + show_y_guides=True, |
| 106 | + range=(-3.5, 3.5), |
| 107 | + xrange=(-3.5, 3.5), |
| 108 | + dots_size=8, # Larger dots for legend visibility |
| 109 | +) |
| 110 | + |
| 111 | +# Add each bin as a series |
| 112 | +for bin_idx in range(4): |
| 113 | + series_data = [] |
| 114 | + for sl in binned_streamlines[bin_idx]: |
| 115 | + # Add streamline points |
| 116 | + for point in sl: |
| 117 | + series_data.append(point) |
| 118 | + # Add None to separate streamlines |
| 119 | + series_data.append(None) |
| 120 | + |
| 121 | + if series_data: |
| 122 | + chart.add(bin_labels[bin_idx], series_data) |
| 123 | + |
| 124 | +# Save outputs |
| 125 | +chart.render_to_png("plot.png") |
| 126 | +chart.render_to_file("plot.html") |
0 commit comments