|
1 | | -""" anyplot.ai |
| 1 | +"""anyplot.ai |
2 | 2 | network-force-directed: Force-Directed Graph |
3 | 3 | Library: pygal 3.1.3 | Python 3.13.14 |
4 | 4 | Quality: 84/100 | Created: 2026-07-01 |
|
46 | 46 | # Intra-module edges (dense within functional groups) |
47 | 47 | for i in range(15): |
48 | 48 | for j in range(i + 1, 15): |
49 | | - if np.random.random() < 0.35: |
| 49 | + if np.random.random() < 0.25: |
50 | 50 | edges.append((i, j)) |
51 | 51 |
|
52 | 52 | for i in range(15, 28): |
53 | 53 | for j in range(i + 1, 28): |
54 | | - if np.random.random() < 0.35: |
| 54 | + if np.random.random() < 0.25: |
55 | 55 | edges.append((i, j)) |
56 | 56 |
|
57 | 57 | for i in range(28, 38): |
58 | 58 | for j in range(i + 1, 38): |
59 | | - if np.random.random() < 0.35: |
| 59 | + if np.random.random() < 0.25: |
60 | 60 | edges.append((i, j)) |
61 | 61 |
|
62 | 62 | # Cross-module interactions (sparse bridges — crosstalk between pathways) |
|
105 | 105 | if disp_norm > 0: |
106 | 106 | positions[i] += (displacement[i] / disp_norm) * min(disp_norm, 0.15 * temperature) |
107 | 107 |
|
108 | | -# Normalize positions to padded plot range |
| 108 | +# Normalize positions with ~15% margin on each side to keep clusters away from canvas edges |
109 | 109 | pos_min = positions.min(axis=0) |
110 | 110 | pos_max = positions.max(axis=0) |
111 | | -positions = (positions - pos_min) / (pos_max - pos_min + 1e-6) * 10 + 1 |
| 111 | +positions = (positions - pos_min) / (pos_max - pos_min + 1e-6) * 9 + 1.5 |
112 | 112 | pos = {node["id"]: positions[i] for i, node in enumerate(nodes)} |
113 | 113 |
|
114 | 114 | # Node degrees (for size encoding) |
|
117 | 117 | degrees[src] += 1 |
118 | 118 | degrees[tgt] += 1 |
119 | 119 |
|
120 | | -# Style — module series use Imprint positions 1-3; edge series uses INK_MUTED (muted semantic anchor) |
| 120 | +# Style — module series use Imprint positions 1-3; intra edges use INK_MUTED; bridge edges use Imprint[3] |
121 | 121 | module_colors = IMPRINT[: len(module_names)] |
122 | | -series_colors = module_colors + (INK_MUTED,) # nodes first → Metabolism gets #009E73 |
| 122 | +BRIDGE_COLOR = IMPRINT[3] # #BD8233 amber — visually distinct cross-module connector |
| 123 | +series_colors = module_colors + (INK_MUTED, BRIDGE_COLOR) # nodes first → Metabolism gets #009E73 |
123 | 124 |
|
124 | 125 | custom_style = Style( |
125 | 126 | background=PAGE_BG, |
|
152 | 153 | show_y_labels=False, |
153 | 154 | stroke=True, |
154 | 155 | dots_size=18, |
155 | | - stroke_style={"width": 1.5, "linecap": "round"}, |
| 156 | + stroke_style={"width": 2.5, "linecap": "round"}, |
156 | 157 | legend_at_bottom=True, |
157 | | - legend_at_bottom_columns=4, |
| 158 | + legend_at_bottom_columns=5, |
158 | 159 | legend_box_size=24, |
159 | 160 | margin=60, |
160 | 161 | range=(0, 12), |
|
177 | 178 | node_points.append({"value": (x, y), "label": label, "node": {"r": round(radius, 1)}}) |
178 | 179 | chart.add(mod_name, node_points, stroke=False) |
179 | 180 |
|
180 | | -# Edge series added LAST (series 3) — uses INK_MUTED via colors position 3 |
181 | | -edge_points = [] |
| 181 | +# Build intra-module edge set for fast lookup |
| 182 | +bridge_edge_set = set(map(tuple, bridge_edges)) |
| 183 | + |
| 184 | +# Intra-module edges (series 3) — uses INK_MUTED via colors position 3 |
| 185 | +intra_edge_points = [] |
182 | 186 | for src, tgt in edges: |
| 187 | + if (src, tgt) not in bridge_edge_set and (tgt, src) not in bridge_edge_set: |
| 188 | + x1, y1 = pos[src] |
| 189 | + x2, y2 = pos[tgt] |
| 190 | + intra_edge_points.append((x1, y1)) |
| 191 | + intra_edge_points.append((x2, y2)) |
| 192 | + intra_edge_points.append(None) |
| 193 | + |
| 194 | +chart.add("Interactions", intra_edge_points, stroke=True, show_dots=False, fill=False) |
| 195 | + |
| 196 | +# Cross-module bridge edges (series 4) — amber #BD8233, dashed to signal inter-pathway crosstalk |
| 197 | +bridge_edge_points = [] |
| 198 | +for src, tgt in bridge_edges: |
183 | 199 | x1, y1 = pos[src] |
184 | 200 | x2, y2 = pos[tgt] |
185 | | - edge_points.append((x1, y1)) |
186 | | - edge_points.append((x2, y2)) |
187 | | - edge_points.append(None) |
| 201 | + bridge_edge_points.append((x1, y1)) |
| 202 | + bridge_edge_points.append((x2, y2)) |
| 203 | + bridge_edge_points.append(None) |
188 | 204 |
|
189 | | -chart.add("Interactions", edge_points, stroke=True, show_dots=False, fill=False) |
| 205 | +chart.add( |
| 206 | + "Cross-module Bridges", |
| 207 | + bridge_edge_points, |
| 208 | + stroke=True, |
| 209 | + show_dots=False, |
| 210 | + fill=False, |
| 211 | + stroke_style={"width": 2.5, "dasharray": "8 5", "linecap": "round"}, |
| 212 | +) |
190 | 213 |
|
191 | 214 | # Save outputs (theme-aware filenames) |
192 | 215 | chart.render_to_file(f"plot-{THEME}.svg") |
|
0 commit comments