import gradio as gr import networkx as nx import matplotlib.pyplot as plt import random import time from graphGen7 import NetworkGenerator def get_preset_dims(preset_mode, topology): """Calculates dimensions based on preset and topology.""" if preset_mode == "Custom": # Enable inputs, keep current values (passed via state in UI, but here we just return interaction update) return gr.update(interactive=True), gr.update(interactive=True) # Preset Logic if topology == "linear": if preset_mode == "Small": dims = (4, 4) elif preset_mode == "Medium": dims = (6, 11) else: dims = (10, 26) else: # Highly Connected / Bottlenecks if preset_mode == "Small": dims = (4, 4) elif preset_mode == "Medium": dims = (8, 8) else: dims = (16, 16) return gr.update(value=dims[0], interactive=False), gr.update(value=dims[1], interactive=False) def update_void_settings(variant, width, height): """Calculates void fraction based on variant logic.""" if variant == "Custom": return gr.update(interactive=True) # Fixed Logic area = width * height val = 0.60 if area <= 20 else 0.35 return gr.update(value=val, interactive=False) def generate_network_viz(topology, width, height, variant, void_frac): try: variant_code = "F" if variant == "Fixed" else "R" generator = NetworkGenerator( width=width, height=height, variant=variant_code, topology=topology, node_drop_fraction=void_frac ) start = time.time() graph = generator.generate() end = time.time() # Plotting fig, ax = plt.subplots(figsize=(8, 8)) pos = {node: (node[0], node[1]) for node in graph.nodes()} nx.draw_networkx_edges(graph, pos, ax=ax, width=2, alpha=0.6, edge_color="#333") nx.draw_networkx_nodes(graph, pos, ax=ax, node_size=350, node_color="#4F46E5", edgecolors="white", linewidths=1.5) nx.draw_networkx_labels(graph, pos, ax=ax, font_size=7, font_color="white", font_weight="bold") ax.set_xlim(-1, width + 1) ax.set_ylim(-1, height + 1) ax.invert_yaxis() # Camera looks top-down (0 at top) ax.grid(True, linestyle=':', alpha=0.3) ax.set_axis_on() ax.tick_params(left=True, bottom=True, labelleft=False, labelbottom=False) ax.set_title(f"{topology.upper()} | {width}x{height} | Voids: {int(void_frac*100)}%") # Metrics info_text = ( f"**Nodes:** {len(graph.nodes())} | " f"**Edges:** {len(graph.edges())} | " f"**Density:** {nx.density(graph):.2f} | " f"**Time:** {end - start:.3f}s" ) return fig, info_text except Exception as e: return None, f"Error: {str(e)}" with gr.Blocks(title="Spatial Network Generator") as demo: gr.Markdown("# Spatial Network Generator") gr.Markdown("Generate procedural room-like graphs with topological constraints.") with gr.Row(): # --- LEFT COLUMN: CONTROLS --- with gr.Column(scale=1): topology_dd = gr.Dropdown( choices=["highly_connected", "bottlenecks", "linear"], value="highly_connected", label="Topology" ) preset_radio = gr.Radio( choices=["Small", "Medium", "Large", "Custom"], value="Medium", label="Preset Size" ) with gr.Row(): width_num = gr.Number(value=8, label="Width (X)", precision=0, interactive=False) height_num = gr.Number(value=8, label="Height (Y)", precision=0, interactive=False) gr.Markdown("---") variant_dd = gr.Dropdown( choices=["Fixed", "Custom"], value="Fixed", label="Variant", info="Fixed = Standard density. Custom = Random density." ) void_slider = gr.Slider( minimum=0.0, maximum=0.9, value=0.35, step=0.05, label="Void Fraction", interactive=False, info="Percentage of grid to leave empty." ) gen_btn = gr.Button("Generate Network", variant="primary") # --- RIGHT COLUMN: VISUALIZATION --- with gr.Column(scale=2): metrics_out = gr.Markdown("Click Generate to see metrics...") plot_out = gr.Plot(label="Network Visualization") # ========================================== # EVENT LISTENERS # ========================================== # 1. Update Dimensions when Preset or Topology changes # Note: We pass inputs to calculate new dims, and output to width/height inputs input_group_dims = [preset_radio, topology_dd] output_group_dims = [width_num, height_num] preset_radio.change(fn=get_preset_dims, inputs=input_group_dims, outputs=output_group_dims) topology_dd.change(fn=get_preset_dims, inputs=input_group_dims, outputs=output_group_dims) # 2. Update Void Settings when Variant or Dimensions change # (If user switches to Fixed, we lock slider. If dimensions change while Fixed, we recalc 0.60 vs 0.35) input_group_void = [variant_dd, width_num, height_num] variant_dd.change(fn=update_void_settings, inputs=input_group_void, outputs=void_slider) width_num.change(fn=update_void_settings, inputs=input_group_void, outputs=void_slider) height_num.change(fn=update_void_settings, inputs=input_group_void, outputs=void_slider) # 3. Main Generation input_group_gen = [topology_dd, width_num, height_num, variant_dd, void_slider] output_group_gen = [plot_out, metrics_out] gen_btn.click(fn=generate_network_viz, inputs=input_group_gen, outputs=output_group_gen) # Launch if __name__ == "__main__": demo.launch()