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