|
| 1 | +""" pyplots.ai |
| 2 | +network-directed: Directed Network Graph |
| 3 | +Library: plotly 6.5.0 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import plotly.graph_objects as go |
| 9 | + |
| 10 | + |
| 11 | +# Data: Software module dependencies (arrows show import direction) |
| 12 | +np.random.seed(42) |
| 13 | + |
| 14 | +nodes = [ |
| 15 | + {"id": 0, "label": "main", "group": "entry"}, |
| 16 | + {"id": 1, "label": "api", "group": "core"}, |
| 17 | + {"id": 2, "label": "auth", "group": "core"}, |
| 18 | + {"id": 3, "label": "database", "group": "core"}, |
| 19 | + {"id": 4, "label": "models", "group": "data"}, |
| 20 | + {"id": 5, "label": "utils", "group": "helpers"}, |
| 21 | + {"id": 6, "label": "config", "group": "helpers"}, |
| 22 | + {"id": 7, "label": "logging", "group": "helpers"}, |
| 23 | + {"id": 8, "label": "cache", "group": "core"}, |
| 24 | + {"id": 9, "label": "router", "group": "core"}, |
| 25 | + {"id": 10, "label": "middleware", "group": "core"}, |
| 26 | + {"id": 11, "label": "validators", "group": "data"}, |
| 27 | + {"id": 12, "label": "schemas", "group": "data"}, |
| 28 | +] |
| 29 | + |
| 30 | +# Directed edges: (source, target) - arrows point from source to target |
| 31 | +edges = [ |
| 32 | + (0, 1), # main -> api |
| 33 | + (0, 6), # main -> config |
| 34 | + (0, 7), # main -> logging |
| 35 | + (1, 2), # api -> auth |
| 36 | + (1, 9), # api -> router |
| 37 | + (1, 10), # api -> middleware |
| 38 | + (2, 3), # auth -> database |
| 39 | + (2, 5), # auth -> utils |
| 40 | + (3, 4), # database -> models |
| 41 | + (3, 8), # database -> cache |
| 42 | + (4, 12), # models -> schemas |
| 43 | + (5, 7), # utils -> logging |
| 44 | + (6, 7), # config -> logging |
| 45 | + (8, 7), # cache -> logging |
| 46 | + (9, 10), # router -> middleware |
| 47 | + (9, 11), # router -> validators |
| 48 | + (10, 2), # middleware -> auth |
| 49 | + (11, 12), # validators -> schemas |
| 50 | + (12, 5), # schemas -> utils |
| 51 | +] |
| 52 | + |
| 53 | +# Group colors |
| 54 | +group_colors = { |
| 55 | + "entry": "#306998", # Python Blue |
| 56 | + "core": "#FFD43B", # Python Yellow |
| 57 | + "data": "#4ECDC4", # Teal |
| 58 | + "helpers": "#95A5A6", # Gray |
| 59 | +} |
| 60 | + |
| 61 | +# Circular layout for clear visualization |
| 62 | +n_nodes = len(nodes) |
| 63 | +angles = np.linspace(0, 2 * np.pi, n_nodes, endpoint=False) |
| 64 | +radius = 3 |
| 65 | +node_x = radius * np.cos(angles) |
| 66 | +node_y = radius * np.sin(angles) |
| 67 | + |
| 68 | +# Create figure |
| 69 | +fig = go.Figure() |
| 70 | + |
| 71 | +# Add edges as lines with arrows using annotations |
| 72 | +for source, target in edges: |
| 73 | + x0, y0 = node_x[source], node_y[source] |
| 74 | + x1, y1 = node_x[target], node_y[target] |
| 75 | + |
| 76 | + # Calculate direction vector |
| 77 | + dx, dy = x1 - x0, y1 - y0 |
| 78 | + length = np.sqrt(dx**2 + dy**2) |
| 79 | + dx, dy = dx / length, dy / length |
| 80 | + |
| 81 | + # Shorten edge to not overlap with nodes (node radius ~0.4) |
| 82 | + node_radius = 0.4 |
| 83 | + x0_adj = x0 + dx * node_radius |
| 84 | + y0_adj = y0 + dy * node_radius |
| 85 | + x1_adj = x1 - dx * node_radius |
| 86 | + y1_adj = y1 - dy * node_radius |
| 87 | + |
| 88 | + # Add edge line |
| 89 | + fig.add_trace( |
| 90 | + go.Scatter( |
| 91 | + x=[x0_adj, x1_adj], |
| 92 | + y=[y0_adj, y1_adj], |
| 93 | + mode="lines", |
| 94 | + line=dict(width=2, color="#666666"), |
| 95 | + hoverinfo="none", |
| 96 | + showlegend=False, |
| 97 | + ) |
| 98 | + ) |
| 99 | + |
| 100 | +# Add arrowheads using annotations |
| 101 | +for source, target in edges: |
| 102 | + x0, y0 = node_x[source], node_y[source] |
| 103 | + x1, y1 = node_x[target], node_y[target] |
| 104 | + |
| 105 | + # Calculate direction vector |
| 106 | + dx, dy = x1 - x0, y1 - y0 |
| 107 | + length = np.sqrt(dx**2 + dy**2) |
| 108 | + dx, dy = dx / length, dy / length |
| 109 | + |
| 110 | + # Arrow position (at target node edge) |
| 111 | + node_radius = 0.45 |
| 112 | + ax = x1 - dx * node_radius |
| 113 | + ay = y1 - dy * node_radius |
| 114 | + |
| 115 | + fig.add_annotation( |
| 116 | + x=x1 - dx * node_radius, |
| 117 | + y=y1 - dy * node_radius, |
| 118 | + ax=x1 - dx * (node_radius + 0.3), |
| 119 | + ay=y1 - dy * (node_radius + 0.3), |
| 120 | + xref="x", |
| 121 | + yref="y", |
| 122 | + axref="x", |
| 123 | + ayref="y", |
| 124 | + showarrow=True, |
| 125 | + arrowhead=2, |
| 126 | + arrowsize=2, |
| 127 | + arrowwidth=2, |
| 128 | + arrowcolor="#666666", |
| 129 | + ) |
| 130 | + |
| 131 | +# Add nodes by group for legend |
| 132 | +for group in ["entry", "core", "data", "helpers"]: |
| 133 | + group_nodes = [n for n in nodes if n["group"] == group] |
| 134 | + group_x = [node_x[n["id"]] for n in group_nodes] |
| 135 | + group_y = [node_y[n["id"]] for n in group_nodes] |
| 136 | + group_labels = [n["label"] for n in group_nodes] |
| 137 | + |
| 138 | + fig.add_trace( |
| 139 | + go.Scatter( |
| 140 | + x=group_x, |
| 141 | + y=group_y, |
| 142 | + mode="markers+text", |
| 143 | + marker=dict(size=45, color=group_colors[group], line=dict(width=2, color="#333333")), |
| 144 | + text=group_labels, |
| 145 | + textposition="middle center", |
| 146 | + textfont=dict(size=14, color="#333333", family="Arial Black"), |
| 147 | + name=group.capitalize(), |
| 148 | + hovertemplate="<b>%{text}</b><br>Group: " + group + "<extra></extra>", |
| 149 | + ) |
| 150 | + ) |
| 151 | + |
| 152 | +# Update layout |
| 153 | +fig.update_layout( |
| 154 | + title=dict( |
| 155 | + text="Software Module Dependencies · network-directed · plotly · pyplots.ai", |
| 156 | + font=dict(size=28, color="#333333"), |
| 157 | + x=0.5, |
| 158 | + xanchor="center", |
| 159 | + ), |
| 160 | + xaxis=dict(showgrid=False, zeroline=False, showticklabels=False, title="", range=[-4.5, 4.5]), |
| 161 | + yaxis=dict( |
| 162 | + showgrid=False, zeroline=False, showticklabels=False, title="", range=[-4.5, 4.5], scaleanchor="x", scaleratio=1 |
| 163 | + ), |
| 164 | + template="plotly_white", |
| 165 | + showlegend=True, |
| 166 | + legend=dict( |
| 167 | + title=dict(text="Module Groups", font=dict(size=18)), |
| 168 | + font=dict(size=16), |
| 169 | + x=1.02, |
| 170 | + y=0.5, |
| 171 | + yanchor="middle", |
| 172 | + bgcolor="rgba(255,255,255,0.9)", |
| 173 | + bordercolor="#CCCCCC", |
| 174 | + borderwidth=1, |
| 175 | + ), |
| 176 | + margin=dict(l=50, r=180, t=100, b=50), |
| 177 | + plot_bgcolor="white", |
| 178 | +) |
| 179 | + |
| 180 | +# Save as PNG (4800x2700 at scale=3) |
| 181 | +fig.write_image("plot.png", width=1600, height=900, scale=3) |
| 182 | + |
| 183 | +# Save as HTML for interactivity |
| 184 | +fig.write_html("plot.html", include_plotlyjs=True, full_html=True) |
0 commit comments