我开始和 JUNG 一起学习 Graph 建模。我实际上有两个问题:1.有什么方法可以对一些顶点进行分组?比如在他们周围画一个盒子?2. JUNG 支持没有源顶点的边吗?如果这是一个正常的边缘:a -> b。我想说-> b
谢谢您的帮助
您可以使用VisualizationViewer#addPreRenderPaintable
在Paintable
您的VisualizationViewer
. 在绘制任何其他内容之前(即在绘制顶点或边缘之前),将调用此可绘制对象。在此可绘制对象中,您可以计算要分组的顶点的边界框,然后将其简单地绘制为矩形。
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFrame;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
public class JUNGVertexStuffTest
{
public static void main(String[] args)
{
JFrame jf = new JFrame();
final Graph<String, String> g = getGraph();
final VisualizationViewer<String, String> vv =
new VisualizationViewer<String, String>(
new FRLayout<String, String>(g));
final AbstractModalGraphMouse graphMouse =
new DefaultModalGraphMouse<String,String>();
vv.setGraphMouse(graphMouse);
List<String> verticesInBox = Arrays.asList("v0", "v1");
addVertexGroupPainter(vv, verticesInBox);
jf.getContentPane().add(vv);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
private static <V> void addVertexGroupPainter(
final VisualizationViewer<V, ?> vv, final Iterable<V> verticesInBox)
{
vv.addPreRenderPaintable(new VisualizationViewer.Paintable()
{
@Override
public boolean useTransform()
{
return true;
}
@Override
public void paint(Graphics gr)
{
Graphics2D g = (Graphics2D)gr;
Layout<V, ?> layout = vv.getGraphLayout();
AffineTransform transform =
vv.getRenderContext().
getMultiLayerTransformer().
getTransformer(Layer.LAYOUT).
getTransform();
Rectangle2D boundingBox =
computeBoundingBox(verticesInBox, layout, transform);
double d = 20;
Shape rect = new RoundRectangle2D.Double(
boundingBox.getMinX()-d,
boundingBox.getMinY()-d,
boundingBox.getWidth()+d+d,
boundingBox.getHeight()+d+d, d, d);
g.setColor(new Color(255,200,200));
g.fill(rect);
g.setColor(Color.BLACK);
g.draw(rect);
}
});
}
private static <V> Rectangle2D computeBoundingBox(
Iterable<V> vertices, Layout<V, ?> layout, AffineTransform at)
{
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
for (V vertex : vertices)
{
Point2D location = layout.transform(vertex);
at.transform(location, location);
minX = Math.min(minX, location.getX());
minY = Math.min(minY, location.getY());
maxX = Math.max(maxX, location.getX());
maxY = Math.max(maxY, location.getY());
}
return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
}
public static Graph<String, String> getGraph()
{
Graph<String, String> g = new DirectedSparseGraph<String, String>();
g.addVertex("v0");
g.addVertex("v1");
g.addVertex("v2");
g.addVertex("v3");
g.addVertex("v4");
g.addEdge("e0", "v0", "v1");
g.addEdge("e1", "v1", "v2");
g.addEdge("e2", "v2", "v3");
g.addEdge("e3", "v3", "v4");
g.addEdge("e4", "v4", "v0");
g.addEdge("e5", "v1", "v3");
g.addEdge("e6", "v2", "v4");
return g;
}
}
注意:根据您确定应该对哪些顶点进行分组的方式,以及根据布局,可能会有更优雅或更有效的解决方案。但这应该是通用的,甚至可以与动态布局或顶点集一起使用。
关于“没有顶点的边”的问题:不支持这样的边。首先,因为调用时必须指定两个顶点Graph#addEdge(...)
。除此之外,这里的关键问题是如何确定该边缘的方向。当然可以考虑“变通办法”。例如,插入一些特殊的顶点,这些顶点被声明为“虚拟”顶点并被排除在渲染之外(因此,它们的存在只是为了成为这种边的“一个(不可见)端点”。但这会很麻烦。
我的水晶球说,目的是表明一个顶点有“更远的边缘”,其端点根本不相关。如果是这种情况,您可以只绘制一些指示这些边缘的线条。这可以大致完成如下(但这只是一个草图,不应该被认为是一个深刻的建议!这肯定有“更好”的解决方案!)
final Renderer.Vertex<String, String> originalVertexRenderer =
vv.getRenderer().getVertexRenderer();
vv.getRenderer().setVertexRenderer(new Renderer.Vertex<String, String>()
{
@Override
public void paintVertex(RenderContext<String, String> rc,
Layout<String, String> layout, String vertex)
{
if (vertex.equals("v4"))
{
Point2D p = layout.transform(vertex);
Line2D pseudoEdge = new Line2D.Double(
p.getX(), p.getY(), p.getX() + 100, p.getY() + 100);
rc.getGraphicsContext().draw(pseudoEdge);
}
originalVertexRenderer.paintVertex(rc, layout, vertex);
}
});