# metric_visualizations.py import networkx as nx import random def get_cycle_highlights(G): """ Identifies all simple cycles and assigns a distinct neon color to each. Returns a list of dictionaries containing node/edge sets and colors. """ try: cycles = list(nx.simple_cycles(G)) except ImportError: return [] highlights = [] # high-contrast "neon" colors for the glow effect neon_colors = [ "#FF1493", # DeepPink "#00FF00", # Lime "#00FFFF", # Cyan "#FFD700", # Gold "#FF4500", # OrangeRed "#9400D3", # DarkViolet "#32CD32", # LimeGreen "#1E90FF", # DodgerBlue ] for i, path in enumerate(cycles): color = neon_colors[i % len(neon_colors)] # Build the edge list for this specific cycle cycle_edges = [] for j in range(len(path)): u = path[j] v = path[(j + 1) % len(path)] # connect last node back to first cycle_edges.append((u, v)) highlights.append({ "nodes": path, "edges": cycle_edges, "color": color, "width": 8 # offset width slightly so overlapping cycles are visible }) return highlights import networkx as nx def get_single_cycle_highlight(G, cycle_index): """ Highlights ONLY the cycle at the specified index, using a distinct color. """ try: cycles = list(nx.simple_cycles(G)) if cycle_index < 0 or cycle_index >= len(cycles): return [] path = cycles[cycle_index] # Same as 'get_cycle_highlights' neon_colors = [ "#FF1493", # DeepPink "#00C000", # Darker Lime (Readable on white) "#DE52D0", # "#FFD700", # Gold "#FF4500", # OrangeRed "#9400D3", # DarkViolet "#32CD32", # LimeGreen "#060C12", # DodgerBlue ] color = neon_colors[cycle_index % len(neon_colors)] cycle_edges = [] for j in range(len(path)): u = path[j] v = path[(j + 1) % len(path)] cycle_edges.append((u, v)) return [{ "nodes": path, "edges": cycle_edges, "color": color, "width": 10 }] except Exception as e: print(f"Error highlighting cycle {cycle_index}: {e}") return [] def get_interdependence_highlights(G): """ Identifies edges that cross agent boundaries (the drivers of interdependence). """ cross_edges = [] involved_nodes = set() for u, v in G.edges(): agent_u = G.nodes[u].get('agent', 'Unassigned') agent_v = G.nodes[v].get('agent', 'Unassigned') if agent_u != agent_v: cross_edges.append((u, v)) involved_nodes.add(u) involved_nodes.add(v) if not cross_edges: return [] return [{ "nodes": list(involved_nodes), "edges": cross_edges, "color": "#FF0000", # highlight color of interdependency "width": 8 }] def get_modularity_highlights(G): """ Detects communities and assigns a unique color to each group. Colors nodes and 'intra-community' edges (edges within the same group). """ try: # 1. Detect Communities # Return list of sets: [{n1, n2}, {n3, n4}...] communities = nx.community.greedy_modularity_communities(G.to_undirected()) highlights = [] # 2. Define Palette community_colors = [ "#FF6B6B", # Red "#4ECDC4", # Teal "#45B7D1", # Blue "#FFA07A", # Light Salmon "#98D8C8", # Pale Green "#F7DC6F", # Yellow "#BB8FCE", # Purple "#B2BABB", # Gray ] for i, community_set in enumerate(communities): color = community_colors[i % len(community_colors)] nodes_list = list(community_set) # Find edges that stay completely within this community intra_edges = [] for u in nodes_list: for v in nodes_list: if G.has_edge(u, v): intra_edges.append((u, v)) # Create Highlight Group highlights.append({ "nodes": nodes_list, "edges": intra_edges, "color": color, "width": 10 }) return highlights except Exception as e: print(f"Modularity Vis Error: {e}") return [] def get_single_modularity_highlight(G, group_index): """ Highlights ONLY the specific community group at the given index. """ try: # 1. Detect Communities communities = list(nx.community.greedy_modularity_communities(G.to_undirected())) # Sort by size communities.sort(key=len, reverse=True) if group_index < 0 or group_index >= len(communities): return [] target_group = list(communities[group_index]) # 2. Match Colors community_colors = [ "#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A", "#98D8C8", "#F7DC6F", "#BB8FCE", "#B2BABB" ] color = community_colors[group_index % len(community_colors)] # 3. Find edges within this group intra_edges = [] for u in target_group: for v in target_group: if G.has_edge(u, v): intra_edges.append((u, v)) return [{ "nodes": target_group, "edges": intra_edges, "color": color, "width": 10 }] except Exception as e: print(f"Modularity Single Error: {e}") return []