Вы можете использовать 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);
}
});
person
Marco13
schedule
08.04.2014