org.jungrapht.samples.tree.SatelliteViewTreeDemo Maven / Gradle / Ivy
Show all versions of jungrapht-visualization-samples Show documentation
/*
* Copyright (c) 2003, The JUNG Authors
* All rights reserved.
*
* This software is open-source under the BSD license; see either "license.txt"
* or https://github.com/tomnelson/jungrapht-visualization/blob/master/LICENSE for a description.
*
*/
package org.jungrapht.samples.tree;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ItemEvent;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import org.jgrapht.Graph;
import org.jgrapht.Graphs;
import org.jungrapht.samples.util.ControlHelpers;
import org.jungrapht.samples.util.DemoTreeSupplier;
import org.jungrapht.visualization.MultiLayerTransformer.Layer;
import org.jungrapht.visualization.SatelliteVisualizationViewer;
import org.jungrapht.visualization.VisualizationModel;
import org.jungrapht.visualization.VisualizationScrollPane;
import org.jungrapht.visualization.VisualizationServer;
import org.jungrapht.visualization.VisualizationViewer;
import org.jungrapht.visualization.annotations.MultiSelectedVertexPaintable;
import org.jungrapht.visualization.annotations.SingleSelectedVertexPaintable;
import org.jungrapht.visualization.decorators.EdgeShape;
import org.jungrapht.visualization.decorators.PickableElementPaintFunction;
import org.jungrapht.visualization.layout.algorithms.TidierTreeLayoutAlgorithm;
import org.jungrapht.visualization.layout.algorithms.TreeLayout;
import org.jungrapht.visualization.renderers.GradientVertexRenderer;
import org.jungrapht.visualization.renderers.Renderer;
import org.jungrapht.visualization.transform.shape.ShapeTransformer;
/**
* Demonstrates the construction of a graph visualization with a main and a satellite view. The
* satellite view is smaller, always contains the entire graph, and contains a lens shape that shows
* the boundaries of the visible part of the graph in the main view. Using the mouse, you can pick,
* translate, layout-scale, view-scale, rotate, shear, and region-select in either view. Using the
* mouse in either window affects only the main view and the lens shape in the satellite view.
*
* @author Tom Nelson
*/
public class SatelliteViewTreeDemo extends JPanel {
static final String instructions =
""
+ "Instructions for Mouse Listeners
"
+ "There are two modes, Transforming and Picking."
+ "
The modes are selected with a combo box."
+ "
Transforming Mode:"
+ "
"
+ "- Mouse1+drag pans the graph"
+ "
- Mouse1+Shift+drag rotates the graph"
+ "
- Mouse1+CTRL(or Command)+drag shears the graph"
+ "
"
+ "Picking Mode:"
+ ""
+ "- Mouse1 on a Vertex selects the vertex"
+ "
- Mouse1 elsewhere unselects all Vertices"
+ "
- Mouse1+Shift on a Vertex adds/removes Vertex selection"
+ "
- Mouse1+drag on a Vertex moves all selected Vertices"
+ "
- Mouse1+drag elsewhere selects Vertices in a region"
+ "
- Mouse1+Shift+drag adds selection of Vertices in a new region"
+ "
- Mouse1+CTRL on a Vertex selects the vertex and centers the display on it"
+ "
"
+ "Both Modes:"
+ ""
+ "- Mousewheel scales with a crossover value of 1.0.
"
+ " - scales the graph layout when the combined scale is greater than 1
"
+ " - scales the graph view when the combined scale is less than 1"
+ "
- Mousewheel scales x and y independently.
"
+ " - CTRL+MouseWheel scales in the X axis
"
+ " - ALT+MouseWheel scales in the Y axis";
JDialog helpDialog;
VisualizationServer.Paintable viewGrid;
/** create an instance of a simple graph in two views with controls to demo the features. */
public SatelliteViewTreeDemo() {
setLayout(new BorderLayout());
// create a simple graph for the demo
Graph graph = DemoTreeSupplier.createForest();
// the preferred sizes for the two views
Dimension preferredSize1 = new Dimension(1000, 1000);
Dimension preferredSize2 = new Dimension(250, 250);
Dimension layoutSize = new Dimension(500, 500);
// create one layout for the graph
TreeLayout layoutAlgorithm = new TidierTreeLayoutAlgorithm<>();
// create one model that both views will share
VisualizationModel vm =
VisualizationModel.builder(graph)
.layoutSize(layoutSize)
.layoutAlgorithm(layoutAlgorithm)
.build();
// create 2 views that share the same model
final VisualizationViewer mainVisualizationViewer =
VisualizationViewer.builder(vm).viewSize(preferredSize1).build();
final SatelliteVisualizationViewer satelliteVisualizationViewer =
SatelliteVisualizationViewer.builder(mainVisualizationViewer)
.viewSize(preferredSize2)
.build();
MultiSelectedVertexPaintable multiSelectedVertexPaintable =
MultiSelectedVertexPaintable.builder(mainVisualizationViewer).build();
mainVisualizationViewer.addPreRenderPaintable(multiSelectedVertexPaintable);
SingleSelectedVertexPaintable singleSelectedVertexPaintable =
SingleSelectedVertexPaintable.builder(mainVisualizationViewer)
.selectedVertexFunction(
vs -> {
Graph g = vs.getVisualizationModel().getGraph();
Set selected = vs.getSelectedVertices();
List roots = new ArrayList<>();
for (String v : selected) {
List predecessors = Graphs.predecessorListOf(g, v);
if (predecessors.isEmpty()) {
roots.add(v);
}
if (!selected.containsAll(predecessors)) {
roots.add(v);
}
}
return roots.isEmpty() ? null : roots.get(0);
})
.build();
mainVisualizationViewer.addPreRenderPaintable(singleSelectedVertexPaintable);
mainVisualizationViewer.getRenderContext().setEdgeStrokeFunction(e -> new BasicStroke(e));
mainVisualizationViewer
.getRenderContext()
.setEdgeDrawPaintFunction(
new PickableElementPaintFunction<>(
mainVisualizationViewer.getSelectedEdgeState(), Color.black, Color.cyan));
mainVisualizationViewer
.getRenderContext()
.setVertexFillPaintFunction(
new PickableElementPaintFunction<>(
mainVisualizationViewer.getSelectedVertexState(), Color.red, Color.yellow));
satelliteVisualizationViewer
.getRenderContext()
.setEdgeDrawPaintFunction(
new PickableElementPaintFunction<>(
satelliteVisualizationViewer.getSelectedEdgeState(), Color.black, Color.cyan));
satelliteVisualizationViewer
.getRenderContext()
.setVertexFillPaintFunction(
new PickableElementPaintFunction<>(
satelliteVisualizationViewer.getSelectedVertexState(), Color.red, Color.yellow));
mainVisualizationViewer
.getRenderer()
.setVertexRenderer(new GradientVertexRenderer<>(Color.red, Color.white, true));
mainVisualizationViewer.getRenderContext().setVertexLabelFunction(Object::toString);
mainVisualizationViewer
.getRenderContext()
.setVertexLabelPosition(Renderer.VertexLabel.Position.CNTR);
mainVisualizationViewer.getRenderContext().setEdgeShapeFunction((g, e) -> EdgeShape.LINE);
satelliteVisualizationViewer.getRenderContext().setEdgeShapeFunction((g, e) -> EdgeShape.LINE);
mainVisualizationViewer.scaleToLayout();
satelliteVisualizationViewer.scaleToLayout();
viewGrid = new ViewGrid(satelliteVisualizationViewer, mainVisualizationViewer);
// add default listener for ToolTips
mainVisualizationViewer.setVertexToolTipFunction(Object::toString);
satelliteVisualizationViewer.setVertexToolTipFunction(Object::toString);
satelliteVisualizationViewer
.getRenderContext()
.setVertexLabelFunction(
mainVisualizationViewer.getRenderContext().getVertexLabelFunction());
ToolTipManager.sharedInstance().setDismissDelay(10000);
Container panel = new JPanel(new BorderLayout());
Container rightPanel = new JPanel(new BorderLayout());
VisualizationScrollPane visualizationScrollPane =
new VisualizationScrollPane(mainVisualizationViewer);
panel.add(visualizationScrollPane);
rightPanel.add(new JPanel());
rightPanel.add(satelliteVisualizationViewer.getComponent(), BorderLayout.SOUTH);
panel.add(rightPanel, BorderLayout.EAST);
helpDialog = new JDialog();
helpDialog.getContentPane().add(new JLabel(instructions));
JCheckBox gridBox = new JCheckBox("Show Grid");
gridBox.addItemListener(
e -> showGrid(satelliteVisualizationViewer, e.getStateChange() == ItemEvent.SELECTED));
JButton help = new JButton("Help");
help.addActionListener(
e -> {
helpDialog.pack();
helpDialog.setVisible(true);
});
JPanel controls = new JPanel();
controls.add(
ControlHelpers.getCenteredContainer(
"Scale", ControlHelpers.getZoomControls(mainVisualizationViewer)));
controls.add(gridBox);
controls.add(help);
add(panel);
add(controls, BorderLayout.SOUTH);
}
protected void showGrid(VisualizationViewer, ?> vv, boolean state) {
if (state) {
vv.addPreRenderPaintable(viewGrid);
} else {
vv.removePreRenderPaintable(viewGrid);
}
vv.repaint();
}
/**
* draws a grid on the SatelliteViewer's lens
*
* @author Tom Nelson
*/
static class ViewGrid implements VisualizationServer.Paintable {
VisualizationViewer, ?> master;
VisualizationViewer, ?> vv;
public ViewGrid(VisualizationViewer, ?> vv, VisualizationViewer, ?> master) {
this.vv = vv;
this.master = master;
}
public void paint(Graphics g) {
ShapeTransformer masterViewTransformer =
master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW);
ShapeTransformer masterLayoutTransformer =
master.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
ShapeTransformer vvLayoutTransformer =
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
Rectangle rect = master.getBounds();
GeneralPath path = new GeneralPath();
path.moveTo(rect.x, rect.y);
path.lineTo(rect.width, rect.y);
path.lineTo(rect.width, rect.height);
path.lineTo(rect.x, rect.height);
path.lineTo(rect.x, rect.y);
for (int i = 0; i <= rect.width; i += rect.width / 10) {
path.moveTo(rect.x + i, rect.y);
path.lineTo(rect.x + i, rect.height);
}
for (int i = 0; i <= rect.height; i += rect.height / 10) {
path.moveTo(rect.x, rect.y + i);
path.lineTo(rect.width, rect.y + i);
}
Shape lens = path;
lens = masterViewTransformer.inverseTransform(lens);
lens = masterLayoutTransformer.inverseTransform(lens);
lens = vvLayoutTransformer.transform(lens);
Graphics2D g2d = (Graphics2D) g;
Color old = g.getColor();
g.setColor(Color.cyan);
g2d.draw(lens);
path = new GeneralPath();
path.moveTo((float) rect.getMinX(), (float) rect.getCenterY());
path.lineTo((float) rect.getMaxX(), (float) rect.getCenterY());
path.moveTo((float) rect.getCenterX(), (float) rect.getMinY());
path.lineTo((float) rect.getCenterX(), (float) rect.getMaxY());
Shape crosshairShape = path;
crosshairShape = masterViewTransformer.inverseTransform(crosshairShape);
crosshairShape = masterLayoutTransformer.inverseTransform(crosshairShape);
crosshairShape = vvLayoutTransformer.transform(crosshairShape);
g.setColor(Color.black);
g2d.setStroke(new BasicStroke(3));
g2d.draw(crosshairShape);
g.setColor(old);
}
public boolean useTransform() {
return true;
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new SatelliteViewTreeDemo());
f.pack();
f.setVisible(true);
}
}