From a2b8880fb63798af2741bb3c091d548dc597675a Mon Sep 17 00:00:00 2001
From: Philipp Spohr <spohr.philipp@web.de>
Date: Fri, 1 Sep 2017 20:20:45 +0200
Subject: [PATCH] Implemented graphic visualization of cluster in solution view
 Various small fixes / stuff

---
 .../de/hhu/ba/yoshikoWrapper/CyActivator.java |   5 +-
 .../ba/yoshikoWrapper/core/AlgorithmTask.java |   2 +-
 .../de/hhu/ba/yoshikoWrapper/core/CyCore.java |   3 +
 .../graphModel/YoshikoCluster.java            | 149 +++++++++++++++++-
 .../graphModel/YoshikoSolution.java           |  38 ++---
 .../ba/yoshikoWrapper/gui/ClusterView.java    |  42 ++---
 .../hhu/ba/yoshikoWrapper/gui/MainPanel.java  |   9 +-
 src/main/resources/YoshikoStrings.properties  |   1 +
 8 files changed, 196 insertions(+), 53 deletions(-)

diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java b/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java
index aff1010..32a50c0 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/CyActivator.java
@@ -42,6 +42,7 @@ import org.cytoscape.task.visualize.ApplyVisualStyleTaskFactory;
 import org.cytoscape.view.model.CyNetworkViewFactory;
 import org.cytoscape.view.layout.CyLayoutAlgorithmManager;
 import org.cytoscape.view.model.CyNetworkViewManager;
+import org.cytoscape.view.presentation.RenderingEngineFactory;
 import org.cytoscape.view.vizmap.VisualMappingFunctionFactory;
 import org.cytoscape.view.vizmap.VisualMappingManager;
 import org.cytoscape.view.vizmap.VisualStyleFactory;
@@ -85,7 +86,9 @@ public class CyActivator extends AbstractCyActivator {
 		CyCore.continuousMappingFactory = getService(context,VisualMappingFunctionFactory.class, "(mapping.type=continuous)");
 		CyCore.rootNetworkManager = getService(context,CyRootNetworkManager.class);
 		CyCore.applyVisualStyleTaskFactory = getService(context,ApplyVisualStyleTaskFactory.class);
-		
+		//TODO: Not sure how to infer type here
+		CyCore.renderingEngineFactory = getService(context,RenderingEngineFactory.class);
+
 		//Set language according to settings
 		LocalizationManager.switchLanguage(cm.getProperties().getProperty("locale", "enUS"));
 
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java b/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java
index 77b7ca9..ba75280 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/core/AlgorithmTask.java
@@ -182,7 +182,7 @@ public class AlgorithmTask extends AbstractTask {
 					taskMonitor.setStatusMessage("Processing entry "+(l+1)+ " of "+sizeOfCluster);
 					int nodeID = LibraryInterface.IntVector_get(clusterVector, l);
 					CyNode node = nodeMap.indexOf(nodeID); //<<< Another int/long conversion
-					cluster.nodes.add(node);
+					cluster.addNode(node);
 					
 					net.getRow(node).set(columnName, ""+(k+1)); //Add Cluster ID in table (Remove in final version?)
 				}
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/core/CyCore.java b/src/main/java/de/hhu/ba/yoshikoWrapper/core/CyCore.java
index 2bd7ce6..fd3c1cd 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/core/CyCore.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/core/CyCore.java
@@ -23,6 +23,7 @@ package de.hhu.ba.yoshikoWrapper.core;
 
 import org.cytoscape.application.CyApplicationManager;
 import org.cytoscape.application.swing.CySwingApplication;
+import org.cytoscape.model.CyNetwork;
 import org.cytoscape.model.CyNetworkFactory;
 import org.cytoscape.model.CyNetworkManager;
 import org.cytoscape.model.subnetwork.CyRootNetworkManager;
@@ -31,6 +32,7 @@ import org.cytoscape.task.visualize.ApplyVisualStyleTaskFactory;
 import org.cytoscape.view.layout.CyLayoutAlgorithmManager;
 import org.cytoscape.view.model.CyNetworkViewFactory;
 import org.cytoscape.view.model.CyNetworkViewManager;
+import org.cytoscape.view.presentation.RenderingEngineFactory;
 import org.cytoscape.view.vizmap.VisualMappingFunctionFactory;
 import org.cytoscape.view.vizmap.VisualMappingManager;
 import org.cytoscape.view.vizmap.VisualStyleFactory;
@@ -59,6 +61,7 @@ public class CyCore {
 	public static VisualMappingFunctionFactory continuousMappingFactory;
 	public static CyRootNetworkManager rootNetworkManager;
 	public static ApplyVisualStyleTaskFactory applyVisualStyleTaskFactory;
+	public static RenderingEngineFactory<CyNetwork> renderingEngineFactory;
 
 	//
 }
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java
index 266fe60..f2e3d51 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java
@@ -21,24 +21,167 @@
  ******************************************************************************/
 package de.hhu.ba.yoshikoWrapper.graphModel;
 
+import java.awt.Image;
 import java.util.ArrayList;
+import java.util.List;
 
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+
+import org.cytoscape.model.CyEdge;
 import org.cytoscape.model.CyNetwork;
 import org.cytoscape.model.CyNode;
+import org.cytoscape.model.CyRow;
+import org.cytoscape.model.subnetwork.CyRootNetwork;
+import org.cytoscape.model.subnetwork.CySubNetwork;
+import org.cytoscape.view.layout.CyLayoutAlgorithm;
+import org.cytoscape.view.model.CyNetworkView;
+import org.cytoscape.view.presentation.RenderingEngine;
+import org.cytoscape.work.Task;
+import org.cytoscape.work.TaskIterator;
+import org.cytoscape.work.TaskMonitor;
+
+import de.hhu.ba.yoshikoWrapper.core.CyCore;
+import de.hhu.ba.yoshikoWrapper.core.LocalizationManager;
 
+import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_HEIGHT;
+import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_WIDTH;
 
 public class YoshikoCluster {
 	
 	
-	public final long id;
-	public final CyNetwork originalGraph;
+	private final long id;
+	
+	private ArrayList<CyNode> nodes;
+	private CySubNetwork net;
+		
+	//SYMBOLIC LINKS
+	private final CyNetwork originalGraph;
+
 	
-	public ArrayList<CyNode> nodes;
 	
 	public YoshikoCluster(long id, CyNetwork originalGraph) {
 		this.id = id;
 		this.originalGraph = originalGraph;
 		this.nodes = new ArrayList<CyNode>();		
 	}
+	
+	private void createSubNetwork() {
+		
+		CyRootNetwork originalGraphAsRoot = CyCore.rootNetworkManager.getRootNetwork(originalGraph);
+
+		//Create nested graph and cluster subnet
+		ArrayList<CyEdge> inducedEdges = new ArrayList<CyEdge>();
+		for (CyNode n: nodes) {
+			//Sadly Cytoscape doesnt provide a comfort function here
+			List<CyEdge> adjacentEdges = originalGraph.getAdjacentEdgeList(n, CyEdge.Type.ANY);
+			for (CyEdge e: adjacentEdges) {
+				if (nodes.contains(e.getSource()) && nodes.contains(e.getTarget())) {
+					inducedEdges.add(e);
+				}
+			}
+		}
+		CySubNetwork subnet = originalGraphAsRoot.addSubNetwork(nodes ,inducedEdges);
+
+		subnet.getRow(subnet).set(CyNetwork.NAME, LocalizationManager.get("cluster")+" "+(id+1));
+		
+		net = subnet; //Save for further reference		
+	}
+	
+	public void select() {
+		List<CyRow> allRows = originalGraph.getDefaultNodeTable().getAllRows();
+		for (CyRow r: allRows) {
+			r.set("selected", false);
+		}
+		//Select nodes corresponding to the cluster
+		for (CyNode n : nodes) {
+			originalGraph.getRow(n).set("selected", true);
+		}
+	}
+	
+
+	public void generateImage(JLabel label, int width, int height) {
+		if (net == null) {
+			createSubNetwork();
+		}
+		CyNetworkView view = CyCore.networkViewFactory.createNetworkView(net);
+		
+		//layout cluster
+		CyLayoutAlgorithm layout = CyCore.layoutAlgorithmManager.getDefaultLayout();
+		TaskIterator createLayout = layout.createTaskIterator(
+			view, 
+			layout.getDefaultLayoutContext(), 
+			CyLayoutAlgorithm.ALL_NODE_VIEWS,
+			null
+		);
+		
+		createLayout.append(
+			new Task() {
+
+				@Override
+				public void run(TaskMonitor taskMonitor) throws Exception {
+					taskMonitor.setStatusMessage("Generating cluster view for C:"+id);
+					view.setVisualProperty(NETWORK_WIDTH, new Double(width));
+					view.setVisualProperty(NETWORK_HEIGHT, new Double(height));
+					view.fitContent();
+					view.updateView();
+										
+					RenderingEngine<CyNetwork> renderingEngine = CyCore.renderingEngineFactory.createRenderingEngine(label, view);
+					SwingUtilities.invokeLater(new Runnable() {
+
+						@Override
+						public void run() {
+							Image img = renderingEngine.createImage(width,height);
+							label.setIcon(new ImageIcon(img));
+							
+							renderingEngine.dispose();
+							view.dispose();
+						}
+						
+					});
+
+					
+				}
+
+				@Override
+				public void cancel() {
+					// TODO Auto-generated method stub
+				}
+				
+			}
+	    );
+		
+		CyCore.dialogTaskManager.execute(
+				createLayout
+		);
+		
+
+	}
+
+	public int getSize() {
+		return nodes.size();
+	}
+
+	public long getID() {
+		return id;
+	}
+
+	public CySubNetwork getSubNetwork() {
+		if (net == null) {
+			createSubNetwork();
+		}
+		return net;
+	}
+
+	public void addNode(CyNode node) {
+		nodes.add(node);
+	}
+
+	public String getNodeName(CyNode n) {
+		return originalGraph.getRow(n).get("name", String.class);
+	}
+
+
 
 }
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java
index 2b1237c..89c6788 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java
@@ -23,12 +23,10 @@ package de.hhu.ba.yoshikoWrapper.graphModel;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 
 import org.cytoscape.model.CyEdge;
 import org.cytoscape.model.CyNetwork;
 import org.cytoscape.model.CyNode;
-import org.cytoscape.model.subnetwork.CyRootNetwork;
 import org.cytoscape.model.subnetwork.CySubNetwork;
 import org.cytoscape.view.layout.CyLayoutAlgorithm;
 import org.cytoscape.view.model.CyNetworkView;
@@ -68,27 +66,11 @@ public class YoshikoSolution {
 		//Map Cluster to Nodes for further reference
 		HashMap<YoshikoCluster,CyNode> map = new HashMap<YoshikoCluster,CyNode>();
 		
-		//Convert original graph to root graph to allow embedding of nodes
-		CyRootNetwork originalGraphAsRoot = CyCore.rootNetworkManager.getRootNetwork(originalGraph);
 		
 		//Add nodes
 		for (YoshikoCluster c: cluster) {
 			CyNode clusterNode = metaGraph.addNode();
-			//Create nested graph and cluster subnet
-			ArrayList<CyEdge> inducedEdges = new ArrayList<CyEdge>();
-			for (CyNode n: c.nodes) {
-				//Sadly Cytoscape doesnt provide a comfort function here
-				List<CyEdge> adjacentEdges = originalGraph.getAdjacentEdgeList(n, CyEdge.Type.ANY);
-				for (CyEdge e: adjacentEdges) {
-					if (c.nodes.contains(e.getSource()) && c.nodes.contains(e.getTarget())) {
-						inducedEdges.add(e);
-					}
-				}
-			}
-			CySubNetwork subnet = originalGraphAsRoot.addSubNetwork(c.nodes ,inducedEdges);
-
-			subnet.getRow(subnet).set(CyNetwork.NAME, LocalizationManager.get("cluster")+" "+(c.id+1));
-			
+			CySubNetwork subnet = c.getSubNetwork();
 			CyCore.networkManager.addNetwork(subnet);
 			//Create network view and register it
 			CyNetworkView subnetView = CyCore.networkViewFactory.createNetworkView(subnet);
@@ -104,8 +86,8 @@ public class YoshikoSolution {
 			);
 			clusterNode.setNetworkPointer(subnet);
 			//Set node attributes
-			metaGraph.getRow(clusterNode).set("name", LocalizationManager.get("cluster")+" "+c.id);
-			metaGraph.getRow(clusterNode).set("clusterSize",c.nodes.size());
+			metaGraph.getRow(clusterNode).set("name", LocalizationManager.get("cluster")+" "+c.getID());
+			metaGraph.getRow(clusterNode).set("clusterSize",c.getSize());
 			map.put(c, clusterNode);
 		}
 		
@@ -118,12 +100,16 @@ public class YoshikoSolution {
 						) {
 					continue;
 				}
-				clusterloop:
-				for (CyNode c1n : c1.nodes) {
-					for (CyNode c2n : c2.nodes) {
+				for (CyNode c1n : c1.getSubNetwork().getNodeList()) {
+					for (CyNode c2n : c2.getSubNetwork().getNodeList()) {
 						if (originalGraph.containsEdge(c1n, c2n) || originalGraph.containsEdge(c2n, c1n)) {
-							metaGraph.addEdge(map.get(c1), map.get(c2), false);
-							break clusterloop;
+							if (metaGraph.containsEdge(map.get(c1), map.get(c2))){
+								//TODO: Update weight
+							}
+							else {
+								CyEdge edge = metaGraph.addEdge(map.get(c1), map.get(c2), false);
+								metaGraph.getRow(edge).set("edgeStrength",1);
+							}
 						}
 					}
 				}
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ClusterView.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ClusterView.java
index 328fa43..91ff582 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ClusterView.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/ClusterView.java
@@ -24,7 +24,6 @@ package de.hhu.ba.yoshikoWrapper.gui;
 import java.awt.Color;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
-import java.util.List;
 
 import javax.swing.BorderFactory;
 import javax.swing.Box;
@@ -34,8 +33,9 @@ import javax.swing.JPanel;
 import javax.swing.border.Border;
 
 import org.cytoscape.model.CyNode;
-import org.cytoscape.model.CyRow;
+import org.cytoscape.util.swing.BasicCollapsiblePanel;
 
+import de.hhu.ba.yoshikoWrapper.core.GraphicsLoader;
 import de.hhu.ba.yoshikoWrapper.core.LocalizationManager;
 import de.hhu.ba.yoshikoWrapper.graphModel.YoshikoCluster;
 
@@ -44,9 +44,11 @@ public class ClusterView extends JPanel {
 	
 	private final JLabel title;
 	private final JLabel clusterSize;
+	private final JLabel icon;
+	private final BasicCollapsiblePanel nodeList;
 	
-	private final Border regularBorder = BorderFactory.createLineBorder(Color.GRAY);
-	private final Border highlightBorder = BorderFactory.createLineBorder(Color.RED);
+	private final Border regularBorder = BorderFactory.createLineBorder(Color.GRAY,3);
+	private final Border highlightBorder = BorderFactory.createLineBorder(GraphicsLoader.yoshikoGreen,3);
 	
 	//SYMBOLIC LINKS
 	
@@ -62,21 +64,28 @@ public class ClusterView extends JPanel {
 		//Swing init
 		this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
 		
-		title = new JLabel(LocalizationManager.get("cluster")+" "+(c.id+1));
-		clusterSize = new JLabel(LocalizationManager.get("clusterSize")+" "+c.nodes.size());
-				
-		SwingUtil.addAll(this,title,clusterSize);
+		title = new JLabel(LocalizationManager.get("cluster")+" "+(c.getID()+1));
+		clusterSize = new JLabel(LocalizationManager.get("clusterSize")+" "+c.getSize());
+		icon = new JLabel();
+		icon.setBorder(BorderFactory.createLineBorder(Color.BLACK));
+		cluster.generateImage(icon,128, 128);
+
+		SwingUtil.addAll(this,title,clusterSize,icon);
 		this.add(Box.createVerticalStrut(4));
 		
+		nodeList = new BasicCollapsiblePanel(LocalizationManager.get("nodes"));
+		
 		//Loop over nodes in the cluster and add them to the view
-		for (CyNode n : c.nodes) {
-			this.add(
+		for (CyNode n : c.getSubNetwork().getNodeList()) {
+			nodeList.add(
 					new JLabel(
-							c.originalGraph.getRow(n).get("name", String.class)
+							c.getNodeName(n)
 					)
 				);
 		}
 		
+		this.add(nodeList);
+		
 		this.addMouseListener(mouseListener);
 		
 		this.setBorder(regularBorder);
@@ -90,16 +99,7 @@ public class ClusterView extends JPanel {
 
 		@Override
 		public void mousePressed(MouseEvent e) {
-			//Undo previous selection
-			
-			List<CyRow> allRows = cluster.originalGraph.getDefaultNodeTable().getAllRows();
-			for (CyRow r: allRows) {
-				r.set("selected", false);
-			}
-			//Select nodes corresponding to the cluster
-			for (CyNode n : cluster.nodes) {
-				cluster.originalGraph.getRow(n).set("selected", true);
-			}
+			cluster.select();
 		}
 		
 		@Override
diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java
index cbfa232..806fccd 100644
--- a/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java
+++ b/src/main/java/de/hhu/ba/yoshikoWrapper/gui/MainPanel.java
@@ -181,6 +181,7 @@ public class MainPanel extends JPanel implements CytoPanelComponent {
 	 */
 	private ActionListener buttonListener = new ActionListener() {
 
+		private final TaskIterator iterator = new TaskIterator();
 		@Override
 		public void actionPerformed(ActionEvent e) {
 			if (YoshikoLoader.isLibraryLoaded()){
@@ -199,7 +200,13 @@ public class MainPanel extends JPanel implements CytoPanelComponent {
 					opModePanel.useHeuristic(),
 					opModePanel.getSolCount()
 				);
-				CyCore.dialogTaskManager.execute(new TaskIterator(1,yoshiko));
+				/**
+				 * Don't queue multiple runs at once because that will get messy
+				 */
+				if (!iterator.hasNext()) {
+					iterator.append(yoshiko);
+					CyCore.dialogTaskManager.execute(iterator);
+				}
 			}
 			else {
 				JOptionPane.showMessageDialog(	
diff --git a/src/main/resources/YoshikoStrings.properties b/src/main/resources/YoshikoStrings.properties
index fa80efd..d7ed53d 100644
--- a/src/main/resources/YoshikoStrings.properties
+++ b/src/main/resources/YoshikoStrings.properties
@@ -52,3 +52,4 @@ switchLanguage = Plugin language
 icTooltip = This value is used to determine what the algorithm pays when inserting an edge. Existing mappings overwrite this value. A higher value means that the algorithm is less likely to insert edges in order to generate a cluster.
 operationMode = Operation Mode
 run = Perform Algorithm
+nodes = Nodes
-- 
GitLab