Как я могу сгруппировать вершины в Юнге?

Я начал изучать графовое моделирование с JUNG. На самом деле у меня есть два вопроса: 1. Можно ли как-нибудь сгруппировать некоторые вершины? Например, нарисовать вокруг них рамку? 2. Поддерживает ли JUNG ребра без исходной вершины? Если это нормальное ребро: a -> b. Я хочу сказать -> б

Спасибо за помощь


person Lao Tse    schedule 08.04.2014    source источник


Ответы (1)


Вы можете использовать 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
comment
Спасибо, это работает, однако не поддерживает прокрутку, даже если вы установите для преобразования значение true. Если мышь поддерживает прокрутку, то график будет прокручиваться, а поле — нет. Любое дальнейшее предложение? - person Bruno Bossola; 14.04.2015
comment
@BrunoBossola Я изменил программу, чтобы она устанавливала мышь, позволяющую масштабировать и панорамировать, и учитывала эти преобразования при вычислении ограничивающего прямоугольника. - person Marco13; 14.04.2015