org.jungrapht.samples.large.ShowLayoutsWithDirectedGraphFileImport Maven / Gradle / Ivy
Show all versions of jungrapht-visualization-samples Show documentation
package org.jungrapht.samples.large;
import static java.util.Map.entry;
import static org.jungrapht.visualization.util.Attributed.*;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Paint;
import java.awt.Stroke;
import java.io.File;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import org.jgrapht.Graph;
import org.jgrapht.Graphs;
import org.jgrapht.alg.interfaces.VertexScoringAlgorithm;
import org.jgrapht.alg.scoring.BetweennessCentrality;
import org.jgrapht.alg.scoring.ClosenessCentrality;
import org.jgrapht.alg.scoring.ClusteringCoefficient;
import org.jgrapht.alg.scoring.HarmonicCentrality;
import org.jgrapht.alg.scoring.PageRank;
import org.jgrapht.graph.builder.GraphTypeBuilder;
import org.jungrapht.samples.spatial.RTreeVisualization;
import org.jungrapht.samples.util.*;
import org.jungrapht.visualization.MultiLayerTransformer;
import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.control.DefaultGraphMouse;
import org.jungrapht.visualization.control.DefaultLensGraphMouse;
import org.jungrapht.visualization.decorators.EdgeShape;
import org.jungrapht.visualization.layout.algorithms.BalloonLayoutAlgorithm;
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
import org.jungrapht.visualization.layout.algorithms.RadialTreeLayoutAlgorithm;
import org.jungrapht.visualization.transform.HyperbolicTransformer;
import org.jungrapht.visualization.transform.LayoutLensSupport;
import org.jungrapht.visualization.transform.Lens;
import org.jungrapht.visualization.transform.LensSupport;
import org.jungrapht.visualization.transform.MagnifyTransformer;
import org.jungrapht.visualization.transform.shape.HyperbolicShapeTransformer;
import org.jungrapht.visualization.transform.shape.MagnifyShapeTransformer;
import org.jungrapht.visualization.transform.shape.ViewLensSupport;
import org.jungrapht.visualization.util.LayoutAlgorithmTransition;
import org.jungrapht.visualization.util.LayoutPaintable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Demonstrates several of the graph layout algorithms. Allows the user to interactively select one
* of several graphs, loaded from files using JGrapht io, and one of several layouts, and visualizes
* the combination.
*
* This application has been tested with the graphml sample files in
* https://github.com/melaniewalsh/sample-social-network-datasets
*
* @author Tom Nelson
*/
public class ShowLayoutsWithDirectedGraphFileImport extends JFrame {
private static final Logger log =
LoggerFactory.getLogger(ShowLayoutsWithDirectedGraphFileImport.class);
LayoutPaintable.BalloonRings balloonLayoutRings;
LayoutPaintable.RadialRings radialLayoutRings;
LayoutPaintable.LayoutBounds layoutBounds;
JFileChooser fileChooser;
VisualizationViewer vv;
Paint[] colorArray =
new Paint[] {
Color.red,
Color.green,
Color.blue,
Color.cyan,
Color.magenta,
Color.yellow,
Color.pink,
Color.gray,
Color.darkGray,
Color.lightGray,
Color.orange
};
Function vertexFillPaintFunction = v -> Colors.getColor(v.getAttributeMap());
Function edgeDrawPaintFunction = e -> Color.black;
Graph graph =
GraphTypeBuilder.directed()
.vertexSupplier(new ASSupplier())
.allowingSelfLoops(true)
.allowingMultipleEdges(true)
.edgeSupplier(new AISupplier())
.buildGraph();
public ShowLayoutsWithDirectedGraphFileImport() {
JPanel container = new JPanel(new BorderLayout());
final DefaultGraphMouse graphMouse = new DefaultGraphMouse<>();
vv =
VisualizationViewer.builder(graph)
.layoutSize(new Dimension(1800, 1800))
.viewSize(new Dimension(900, 900))
.graphMouse(graphMouse)
.build();
vv.setVertexToolTipFunction(v -> v.toHtml());
vv.setEdgeToolTipFunction(e -> e.toHtml());
vv.getRenderContext().setVertexLabelFunction(v -> v.getValue());
// vertex -> {
// Map map = vertex.getAttributeMap();
// return map.getOrDefault("label", map.getOrDefault("ID", "NONE"));
// });
Function vertexStrokeFunction =
v ->
vv.getSelectedVertexState().isSelected(v) ? new BasicStroke(8.f) : new BasicStroke(2.f);
vv.getRenderContext().setVertexStrokeFunction(vertexStrokeFunction);
vv.getRenderContext().setEdgeShapeFunction(EdgeShape.line());
vv.scaleToLayout();
container.add(vv.getComponent(), BorderLayout.CENTER);
LayoutHelperDirectedGraphs.Layouts[] combos = LayoutHelperDirectedGraphs.getCombos();
final JToggleButton animateLayoutTransition = new JToggleButton("Animate Layout Transition");
final JComboBox layoutComboBox = new JComboBox(combos);
layoutComboBox.addActionListener(
e ->
SwingUtilities.invokeLater(
() -> {
LayoutHelperDirectedGraphs.Layouts layoutType =
(LayoutHelperDirectedGraphs.Layouts) layoutComboBox.getSelectedItem();
LayoutAlgorithm layoutAlgorithm = layoutType.getLayoutAlgorithm();
vv.removePreRenderPaintable(balloonLayoutRings);
vv.removePreRenderPaintable(radialLayoutRings);
if (animateLayoutTransition.isSelected()) {
LayoutAlgorithmTransition.animate(vv, layoutAlgorithm);
} else {
LayoutAlgorithmTransition.apply(vv, layoutAlgorithm);
}
if (layoutAlgorithm instanceof BalloonLayoutAlgorithm) {
balloonLayoutRings =
new LayoutPaintable.BalloonRings(
vv, (BalloonLayoutAlgorithm) layoutAlgorithm);
vv.addPreRenderPaintable(balloonLayoutRings);
}
if (layoutAlgorithm instanceof RadialTreeLayoutAlgorithm) {
radialLayoutRings =
new LayoutPaintable.RadialRings(
vv, (RadialTreeLayoutAlgorithm) layoutAlgorithm);
vv.addPreRenderPaintable(radialLayoutRings);
}
if (layoutBounds != null) {
vv.removePreRenderPaintable(layoutBounds);
}
vv.addPreRenderPaintable(new LayoutPaintable.LayoutBounds(vv));
}));
layoutComboBox.setSelectedItem(LayoutHelperDirectedGraphs.Layouts.FR_BH_VISITOR);
// create a lens to share between the two hyperbolic transformers
Lens lens = new Lens(); /* provides a Hyperbolic lens for the view */
LensSupport hyperbolicViewSupport =
ViewLensSupport.builder(vv)
.lensTransformer(
HyperbolicShapeTransformer.builder(lens)
.delegate(
vv.getRenderContext()
.getMultiLayerTransformer()
.getTransformer(MultiLayerTransformer.Layer.VIEW))
.build())
.lensGraphMouse(
DefaultLensGraphMouse.builder()
.magnificationFloor(0.4f)
.magnificationCeiling(1.0f)
.magnificationDelta(0.05f)
.build())
.build();
LensSupport hyperbolicLayoutSupport =
LayoutLensSupport.builder(vv)
.lensTransformer(
HyperbolicTransformer.builder(lens)
.delegate(
vv.getRenderContext()
.getMultiLayerTransformer()
.getTransformer(MultiLayerTransformer.Layer.LAYOUT))
.build())
.lensGraphMouse(
DefaultLensGraphMouse.builder()
.magnificationFloor(0.4f)
.magnificationCeiling(1.0f)
.magnificationDelta(0.05f)
.build())
.build();
// the magnification lens uses a different magnification than the hyperbolic lens
// create a new one to share between the two magnigy transformers
lens = new Lens();
lens.setMagnification(3.f);
LensSupport magnifyViewSupport =
ViewLensSupport.builder(vv)
.lensTransformer(
MagnifyShapeTransformer.builder(lens)
.delegate(
vv.getRenderContext()
.getMultiLayerTransformer()
.getTransformer(MultiLayerTransformer.Layer.VIEW))
.build())
.lensGraphMouse( // override with range for magnificaion
DefaultLensGraphMouse.builder()
.magnificationFloor(1.0f)
.magnificationCeiling(4.0f)
.build())
.build();
LensSupport magnifyLayoutSupport =
LayoutLensSupport.builder(vv)
.lensTransformer(
MagnifyTransformer.builder(lens)
.delegate(
vv.getRenderContext()
.getMultiLayerTransformer()
.getTransformer(MultiLayerTransformer.Layer.LAYOUT))
.build())
.lensGraphMouse( // override with range for magnificaion
DefaultLensGraphMouse.builder()
.magnificationFloor(1.0f)
.magnificationCeiling(4.0f)
.build())
.build();
hyperbolicLayoutSupport
.getLensTransformer()
.getLens()
.setLensShape(hyperbolicViewSupport.getLensTransformer().getLens().getLensShape());
magnifyViewSupport
.getLensTransformer()
.getLens()
.setLensShape(hyperbolicLayoutSupport.getLensTransformer().getLens().getLensShape());
magnifyLayoutSupport
.getLensTransformer()
.getLens()
.setLensShape(magnifyViewSupport.getLensTransformer().getLens().getLensShape());
JComponent lensBox =
LensControlHelper.builder(
Map.of(
"Hyperbolic View", hyperbolicViewSupport,
"Hyperbolic Layout", hyperbolicLayoutSupport,
"Magnified View", magnifyViewSupport,
"Magnified Layout", magnifyLayoutSupport))
.containerSupplier(JPanel::new)
.containerLayoutManager(new GridLayout(0, 2))
.title("Lens Controls")
.build()
.container();
JPanel loadFilePanel = new JPanel();
JButton loadFileButton = new JButton("Load File");
loadFilePanel.add(loadFileButton);
loadFileButton.addActionListener(
e -> {
if (fileChooser == null) {
fileChooser = new JFileChooser();
}
int option = fileChooser.showOpenDialog(vv.getComponent());
if (option == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
boolean loaded = ASAILoader.load(file, graph);
if (!loaded) {
JOptionPane.showMessageDialog(vv.getComponent(), "Unable to open " + file.getName());
return;
}
vv.getRenderContext().setVertexFillPaintFunction(vertexFillPaintFunction);
vv.getRenderContext().setEdgeDrawPaintFunction(edgeDrawPaintFunction);
vv.getRenderContext().setArrowFillPaintFunction(edgeDrawPaintFunction);
vv.getRenderContext().setArrowDrawPaintFunction(edgeDrawPaintFunction);
List sorted =
graph
.vertexSet()
.stream()
.sorted(Comparator.comparingInt(v -> graph.degreeOf(v)))
.collect(Collectors.toList());
sorted.forEach(v -> log.info("V: {} degree: {}", v, graph.degreeOf(v)));
vv.getVisualizationModel().setGraph(graph);
setTitle(
"Graph of "
+ file.getName()
+ " with "
+ graph.vertexSet().size()
+ " vertices and "
+ graph.edgeSet().size()
+ " edges");
}
});
JButton pageRankButton = new JButton("Page Rank");
pageRankButton.addActionListener(event -> computeScores(new PageRank(graph)));
JButton betweennessButton = new JButton("Betweenness");
betweennessButton.addActionListener(event -> computeScores(new BetweennessCentrality<>(graph)));
JButton closenessButton = new JButton("Closeness");
closenessButton.addActionListener(event -> computeScores(new ClosenessCentrality<>(graph)));
JButton clusteringButton = new JButton("Clustering");
clusteringButton.addActionListener(event -> computeScores(new ClusteringCoefficient<>(graph)));
JButton harmonicButton = new JButton("Harmonic");
harmonicButton.addActionListener(event -> computeScores(new HarmonicCentrality<>(graph)));
JButton noScores = new JButton("None");
JPanel scoringGrid = new JPanel(new GridLayout(0, 2));
scoringGrid.add(pageRankButton);
scoringGrid.add(betweennessButton);
scoringGrid.add(closenessButton);
scoringGrid.add(clusteringButton);
scoringGrid.add(harmonicButton);
scoringGrid.add(noScores);
JPanel controlPanel = new JPanel(new GridLayout(2, 1));
JComponent top =
ControlHelpers.getContainer(
Box.createHorizontalBox(),
ControlHelpers.getCenteredContainer("Graph Source", loadFilePanel),
ControlHelpers.getCenteredContainer("Layouts", layoutComboBox),
ControlHelpers.getCenteredContainer("Scoring", scoringGrid));
controlPanel.add(top);
JButton showRTree = new JButton("Show RTree");
showRTree.addActionListener(e -> RTreeVisualization.showRTree(vv));
JComponent bottom =
ControlHelpers.getContainer(
Box.createHorizontalBox(),
// ControlHelpers.getZoomControls("Scale", vv),
lensBox,
ControlHelpers.getCenteredContainer(
"Effects", Box.createVerticalBox(), showRTree, animateLayoutTransition));
controlPanel.add(bottom);
container.add(controlPanel, BorderLayout.NORTH);
setTitle(
"Graph With "
+ graph.vertexSet().size()
+ " vertices and "
+ graph.edgeSet().size()
+ " edges");
getContentPane().add(container);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
private void computeScores(VertexScoringAlgorithm scoring) {
Map scores = scoring.getScores();
if (scores.isEmpty()) return;
double min = scores.values().stream().min(Double::compare).get();
double max = scores.values().stream().max(Double::compare).get();
double range = max - min;
log.info("min:{}, max:{}, range:{}", min, max, range);
double delta = range / colorArray.length;
log.info("delta:{}", delta);
vv.repaint();
}
void clear(Graph graph) {
Set edges = new HashSet(graph.edgeSet());
Set vertices = new HashSet(graph.vertexSet());
edges.forEach(graph::removeEdge);
vertices.forEach(graph::removeVertex);
}
/**
* these are vertex or edge types that have defined colors (the keys are the property values for
* the vertex/edge keys: VertexType and EdgeType)
*/
public static Map VERTEX_TYPE_TO_COLOR_MAP =
Map.ofEntries(
entry("Body", Color.blue),
entry("Entry", Colors.WEB_COLOR_MAP.get("DarkOrange")),
entry("Exit", Color.magenta),
entry("Switch", Color.cyan),
entry("Bad", Color.red),
entry("Entry-Nexus", Color.white),
entry("External", Color.green),
entry("Folder", Colors.WEB_COLOR_MAP.get("DarkOrange")),
entry("Fragment", Colors.WEB_COLOR_MAP.get("Purple")),
entry("Data", Color.pink));
/**
* these are vertex or edge types that have defined colors (the keys are the property values for
* the vertex/edge keys: VertexType and EdgeType)
*/
public static Map EDGE_TYPE_TO_COLOR_MAP =
Map.ofEntries(
entry("Entry", Color.gray), // white??
entry("Fall-Through", Color.blue),
entry("Conditional-Call", Colors.WEB_COLOR_MAP.get("DarkOrange")),
entry("Unconditional-Call", Colors.WEB_COLOR_MAP.get("DarkOrange")),
entry("Computed", Color.cyan),
entry("Indirection", Color.pink),
entry("Unconditional-Jump", Color.green),
entry("Conditional-Jump", Color.yellow.darker()),
entry("Terminator", Colors.WEB_COLOR_MAP.get("Purple")),
entry("Conditional-Return", Colors.WEB_COLOR_MAP.get("Purple")));
public static Paint getColor(Map map) {
// Map map = attributed.getAttributeMap();
// if there is a 'VertexType' attribute key, use its value to choose a predefined color
if (map.containsKey("VertexType")) {
String typeValue = map.get("VertexType");
return VERTEX_TYPE_TO_COLOR_MAP.getOrDefault(typeValue, Color.blue);
}
// if there is an 'EdgeType' attribute key, use its value to choose a predefined color
if (map.containsKey("EdgeType")) {
String typeValue = map.get("EdgeType");
return EDGE_TYPE_TO_COLOR_MAP.getOrDefault(typeValue, Color.green);
}
// if there is a 'Color' attribute key, use its value (either a color name or an RGB hex string)
// to choose a color
if (map.containsKey("Color")) {
String colorName = map.get("Color");
if (Colors.WEB_COLOR_MAP.containsKey(colorName)) {
return Colors.WEB_COLOR_MAP.get(colorName);
}
// if the value matches an RGB hex string, turn that into a color
Color c = Colors.getHexColor(colorName);
if (c != null) {
return c;
}
}
// default value when nothing else matches
return Color.green;
}
private Collection getRoots(Graph graph) {
Set roots = new HashSet<>();
for (Object v : graph.vertexSet()) {
if (Graphs.predecessorListOf(graph, v).isEmpty()) {
roots.add(v);
}
}
return roots;
}
public static void main(String[] args) {
new ShowLayoutsWithDirectedGraphFileImport();
}
}