@@ -181,6 +181,16 @@ public class Main extends JFrame {
181181 private JRadioButton pathByHops ;
182182 private JRadioButton pathByWeight ;
183183
184+ // --- MST fields ---
185+ private JPanel mstPanel ;
186+ private JButton mstComputeButton ;
187+ private JButton mstClearButton ;
188+ private JLabel mstSummaryLabel ;
189+ private JLabel mstStatsLabel ;
190+ private JLabel mstComponentsLabel ;
191+ private boolean mstOverlayActive ;
192+ private Set <edge > mstEdges ;
193+
184194 // --- Centrality analysis fields ---
185195 private JPanel centralityPanel ;
186196 private JButton centralityComputeButton ;
@@ -228,6 +238,7 @@ public Main() throws FileNotFoundException, Exception {
228238 initializeStatsPanel ();
229239 initializePathPanel ();
230240 initializeCommunityPanel ();
241+ initializeMSTPanel ();
231242 initializeCentralityPanel ();
232243 initializeTimeLine ();
233244 initializeToolBar ();
@@ -595,6 +606,10 @@ public Paint transform(edge edge) {
595606 if (pathEdges != null && pathEdges .contains (edge )) {
596607 return Color .YELLOW ;
597608 }
609+ // MST overlay — highlight MST edges in bright green
610+ if (mstOverlayActive && mstEdges != null && mstEdges .contains (edge )) {
611+ return new Color (0 , 255 , 100 );
612+ }
598613 // Community overlay mode — color edges by community
599614 if (communityOverlayActive && nodeCommunityMap != null ) {
600615 Integer c1 = nodeCommunityMap .get (edge .getVertex1 ());
@@ -606,6 +621,10 @@ public Paint transform(edge edge) {
606621 }
607622 return new Color (100 , 100 , 100 , 80 ); // cross-community edges are dim
608623 }
624+ // MST overlay — dim non-MST edges
625+ if (mstOverlayActive && mstEdges != null && !mstEdges .contains (edge )) {
626+ return new Color (80 , 80 , 80 , 60 );
627+ }
609628 if (edge .getType ().equalsIgnoreCase ("f" )) {
610629 return FRIEND_COLOR ;
611630 } else if (edge .getType ().equalsIgnoreCase ("fs" )) {
@@ -706,6 +725,10 @@ public Stroke transform(edge i) {
706725 if (pathEdges != null && pathEdges .contains (i )) {
707726 return new BasicStroke (3.0f , BasicStroke .CAP_ROUND , BasicStroke .JOIN_ROUND );
708727 }
728+ // MST edges get thicker solid stroke
729+ if (mstOverlayActive && mstEdges != null && mstEdges .contains (i )) {
730+ return new BasicStroke (2.5f , BasicStroke .CAP_ROUND , BasicStroke .JOIN_ROUND );
731+ }
709732 float dash [] = {1.0f };
710733 float width = i .getWeight () / 40 + 1.0f ;
711734 return new BasicStroke (width , BasicStroke .CAP_BUTT , BasicStroke .JOIN_BEVEL , 10.0f , dash , 0.0f );
@@ -1200,6 +1223,164 @@ private void clearCommunityOverlay() {
12001223 refreshGraph ();
12011224 }
12021225
1226+ /**
1227+ * Initializes the Minimum Spanning Tree panel with compute/clear buttons
1228+ * and result display.
1229+ */
1230+ public final void initializeMSTPanel () {
1231+ mstOverlayActive = false ;
1232+ mstEdges = new HashSet <edge >();
1233+
1234+ mstPanel = new JPanel ();
1235+ mstPanel .setLayout (new BoxLayout (mstPanel , BoxLayout .Y_AXIS ));
1236+ mstPanel .setBorder (BorderFactory .createTitledBorder (
1237+ BorderFactory .createEtchedBorder (EtchedBorder .LOWERED ),
1238+ "Minimum Spanning Tree" ,
1239+ TitledBorder .CENTER ,
1240+ TitledBorder .TOP ));
1241+
1242+ Font labelFont = new Font ("SansSerif" , Font .PLAIN , 12 );
1243+
1244+ mstSummaryLabel = new JLabel ("<html>Click 'Compute' to find the MST.</html>" );
1245+ mstSummaryLabel .setFont (labelFont );
1246+ mstSummaryLabel .setAlignmentX (JLabel .LEFT_ALIGNMENT );
1247+
1248+ mstStatsLabel = new JLabel ("" );
1249+ mstStatsLabel .setFont (labelFont );
1250+ mstStatsLabel .setAlignmentX (JLabel .LEFT_ALIGNMENT );
1251+
1252+ mstComponentsLabel = new JLabel ("" );
1253+ mstComponentsLabel .setFont (labelFont );
1254+ mstComponentsLabel .setAlignmentX (JLabel .LEFT_ALIGNMENT );
1255+
1256+ // Buttons
1257+ mstComputeButton = new JButton ("Compute" );
1258+ mstComputeButton .setAlignmentX (JButton .LEFT_ALIGNMENT );
1259+ mstComputeButton .addActionListener (new ActionListener () {
1260+ public void actionPerformed (ActionEvent e ) {
1261+ runMSTComputation ();
1262+ }
1263+ });
1264+
1265+ mstClearButton = new JButton ("Clear" );
1266+ mstClearButton .setAlignmentX (JButton .LEFT_ALIGNMENT );
1267+ mstClearButton .addActionListener (new ActionListener () {
1268+ public void actionPerformed (ActionEvent e ) {
1269+ clearMSTOverlay ();
1270+ }
1271+ });
1272+
1273+ JPanel buttonPanel = new JPanel ();
1274+ buttonPanel .setLayout (new BoxLayout (buttonPanel , BoxLayout .X_AXIS ));
1275+ buttonPanel .setAlignmentX (JPanel .LEFT_ALIGNMENT );
1276+ buttonPanel .add (mstComputeButton );
1277+ buttonPanel .add (Box .createHorizontalStrut (4 ));
1278+ buttonPanel .add (mstClearButton );
1279+
1280+ mstPanel .add (mstSummaryLabel );
1281+ mstPanel .add (Box .createVerticalStrut (4 ));
1282+ mstPanel .add (buttonPanel );
1283+ mstPanel .add (Box .createVerticalStrut (4 ));
1284+ mstPanel .add (mstStatsLabel );
1285+ mstPanel .add (Box .createVerticalStrut (4 ));
1286+ mstPanel .add (mstComponentsLabel );
1287+ }
1288+
1289+ /**
1290+ * Computes the MST and activates the edge highlight overlay.
1291+ */
1292+ private void runMSTComputation () {
1293+ if (g == null || g .getVertexCount () == 0 ) {
1294+ mstSummaryLabel .setText ("<html>No graph loaded.</html>" );
1295+ return ;
1296+ }
1297+
1298+ MinimumSpanningTree mstComputer = new MinimumSpanningTree (g );
1299+ MinimumSpanningTree .MSTResult result = mstComputer .compute ();
1300+
1301+ mstOverlayActive = true ;
1302+ mstEdges .clear ();
1303+ mstEdges .addAll (result .getEdges ());
1304+
1305+ // Summary
1306+ mstSummaryLabel .setText ("<html><b style='color:#00FF00'>"
1307+ + result .getSummary () + "</b></html>" );
1308+
1309+ // Stats
1310+ StringBuilder stats = new StringBuilder ("<html>" );
1311+ stats .append (String .format ("<b>Vertices:</b> %d<br/>" , result .getVertexCount ()));
1312+ stats .append (String .format ("<b>MST Edges:</b> %d<br/>" , result .getEdgeCount ()));
1313+ stats .append (String .format ("<b>Total Weight:</b> %.1f<br/>" , result .getTotalWeight ()));
1314+ stats .append (String .format ("<b>Avg Weight:</b> %.1f<br/>" , result .getAverageWeight ()));
1315+
1316+ if (result .getHeaviestEdge () != null ) {
1317+ edge heavy = result .getHeaviestEdge ();
1318+ stats .append (String .format ("<b>Bottleneck:</b> %s↔%s (%.1f)<br/>" ,
1319+ heavy .getVertex1 (), heavy .getVertex2 (), heavy .getWeight ()));
1320+ }
1321+ if (result .getLightestEdge () != null ) {
1322+ edge light = result .getLightestEdge ();
1323+ stats .append (String .format ("<b>Lightest:</b> %s↔%s (%.1f)<br/>" ,
1324+ light .getVertex1 (), light .getVertex2 (), light .getWeight ()));
1325+ }
1326+
1327+ // Edge type distribution
1328+ Map <String , Integer > dist = result .getEdgeTypeDistribution ();
1329+ if (!dist .isEmpty ()) {
1330+ stats .append ("<b>Types:</b> " );
1331+ boolean first = true ;
1332+ for (Map .Entry <String , Integer > entry : dist .entrySet ()) {
1333+ if (!first ) stats .append (", " );
1334+ stats .append (getDominantLabel (entry .getKey ())).append ("=" ).append (entry .getValue ());
1335+ first = false ;
1336+ }
1337+ stats .append ("<br/>" );
1338+ }
1339+ stats .append ("</html>" );
1340+ mstStatsLabel .setText (stats .toString ());
1341+
1342+ // Component breakdown (for forests)
1343+ if (result .getComponentCount () > 1 ) {
1344+ StringBuilder comps = new StringBuilder ("<html><b>Components:</b><br/>" );
1345+ int shown = Math .min (result .getComponents ().size (), 6 );
1346+ for (int i = 0 ; i < shown ; i ++) {
1347+ MinimumSpanningTree .MSTComponent comp = result .getComponents ().get (i );
1348+ comps .append (String .format (" C%d: %d nodes, %d edges, wt=%.1f" ,
1349+ comp .getId (), comp .getSize (), comp .getEdges ().size (), comp .getTotalWeight ()));
1350+ String dominant = comp .getDominantType ();
1351+ if (dominant != null ) {
1352+ comps .append (" (" ).append (getDominantLabel (dominant )).append (")" );
1353+ }
1354+ comps .append ("<br/>" );
1355+ }
1356+ if (result .getComponents ().size () > shown ) {
1357+ comps .append ("...and " ).append (result .getComponents ().size () - shown ).append (" more<br/>" );
1358+ }
1359+ comps .append ("</html>" );
1360+ mstComponentsLabel .setText (comps .toString ());
1361+ } else {
1362+ mstComponentsLabel .setText ("" );
1363+ }
1364+
1365+ mstPanel .revalidate ();
1366+ mstPanel .repaint ();
1367+ refreshGraph ();
1368+ }
1369+
1370+ /**
1371+ * Clears the MST overlay and resets the panel.
1372+ */
1373+ private void clearMSTOverlay () {
1374+ mstOverlayActive = false ;
1375+ mstEdges .clear ();
1376+ mstSummaryLabel .setText ("<html>Click 'Compute' to find the MST.</html>" );
1377+ mstStatsLabel .setText ("" );
1378+ mstComponentsLabel .setText ("" );
1379+ mstPanel .revalidate ();
1380+ mstPanel .repaint ();
1381+ refreshGraph ();
1382+ }
1383+
12031384 /**
12041385 * Initializes the centrality analysis panel with compute/clear buttons,
12051386 * metric selector, and ranked results display.
@@ -1518,9 +1699,13 @@ public final void showRightPane() {
15181699 JSplitPane splitPaneCommunity = new JSplitPane (JSplitPane .VERTICAL_SPLIT ,
15191700 splitPanePath , communityPanel );
15201701
1521- // Add centrality panel below community panel
1702+ // Add MST panel below community panel
1703+ JSplitPane splitPaneMST = new JSplitPane (JSplitPane .VERTICAL_SPLIT ,
1704+ splitPaneCommunity , mstPanel );
1705+
1706+ // Add centrality panel below MST panel
15221707 JSplitPane splitPaneCentrality = new JSplitPane (JSplitPane .VERTICAL_SPLIT ,
1523- splitPaneCommunity , centralityPanel );
1708+ splitPaneMST , centralityPanel );
15241709
15251710 // Add stats panel below centrality panel
15261711 JSplitPane splitPane2 = new JSplitPane (JSplitPane .VERTICAL_SPLIT ,
@@ -1529,8 +1714,9 @@ public final void showRightPane() {
15291714 splitPane1 .setDividerLocation (400 );
15301715 splitPanePath .setDividerLocation (510 );
15311716 splitPaneCommunity .setDividerLocation (640 );
1532- splitPaneCentrality .setDividerLocation (820 );
1533- splitPane2 .setDividerLocation (1050 );
1717+ splitPaneMST .setDividerLocation (760 );
1718+ splitPaneCentrality .setDividerLocation (920 );
1719+ splitPane2 .setDividerLocation (1150 );
15341720 add (splitPane2 , BorderLayout .EAST );
15351721
15361722 }
0 commit comments