AWT Knoten für Graphen kreisförmig anordnen

  • Themenstarter Themenstarter dieEule
  • Beginndatum Beginndatum
D

dieEule

Gast
Hallo,

ich habe eine kleine Graphen-Applikation geschrieben mit zwei HashSets (Knoten und Kanten), bei der man z.B. Knoten hinzufügen, Kanten hinzufügen oder Knoten löschen kann (das wird alles über die Methoden in der Klasse Graph geregelt).
Dieses Programm soll nun eine GUI (Klasse View) bekommen, aber ich scheitere an der Anordnung der einzelnen Knoten. Ich möchte sie kreisförmig anordnen, damit sich die Kantenverbindungen nicht überlagern und gut sichtbar sind. Die Anzeige soll sich nach jeder neuen Eingabe aktualisieren und je mehr Knoten es werden, desto größer muss der Radius des Kreises werden.
Hat vielleicht jemand eine Idee, wie ich diese Anordnung möglichst einfach hinkriegen kann? Am besten ohne großen Aufwand mit Layout-Managern etc., da mich das als blutigen Anfänger noch etwas überfordert ;-)
Vielen Dank für eure Ideen! 🙂
dieEule
 
Graphen-Layout ist eine Wissenschaft für sich, aber das "Circle-Layout", was du angedeutet hast, ist wohl noch eins der einfachsten. LayoutManager haben damit natürlich nichts zu tun, aber man braucht schon eine Klasse, die aus einer Menge von Knoten ihre Positionen berechnet. Wie allgemein die sein sollte ... ist AUCH eine Wissenschaft für sich 😀 Gib vielleicht mal mehr Infos: Wer bestimmt bisher die Positionen? Wo werden die Positionen gespeichert (direkt im Knoten)? usw...
 
allein google liefert bei "Circle Layout" tausende Treffer...

wenn dich Layout Manager deiner Meinung nach ueberfordern, dann kannst du ein Circle Layout wegen der Komplexitaet mal knicken.

"Choose your battle" - entweder du machst so was komplexes und gehst den ganzen weg oder du suchst dir etwas einfacheres
 
Die Knoten mit ihrer Position werden in der ArrayList<NodeData> gespeichert, NodeData ist nochmal eine extra Klasse.
Der Code sieht momentan so aus:

Java:
package application.gui;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import objects.Edge;
import objects.Graph;
import objects.Node;


public class View extends JFrame {

	private static final long serialVersionUID = 7094413965996596889L;
	private JFrame frame;
	private JPanel contentPane;
	private final Graph graph;
	private int currentX = 50;
	private int currentY = 50;
	private int stepX = 150;
	private int stepY = 20;
	private ArrayList<NodeData> nodeData; //hier sind Knoten+Position drin gespeichert


	public View(final Graph graph) {
		this.graph = graph;
		frame = new JFrame();
		frame.setBounds(100, 100, 1155, 403);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setTitle("Graphen");
		
		Container container = new Container();
		container.setLayout(null);
		
		//hier steht alles zum Menü, habe ich erstmal rausgenommen...
		frame.setJMenuBar(menuBar);
		
		contentPane = new JPanel();
		contentPane.setPreferredSize(new Dimension(frame.getWidth(), 400));
		contentPane.setBackground(Color.white);
		contentPane.setLayout(null);
		
		container.add(contentPane);
		frame.add(container);
		frame.setContentPane(contentPane);	
		frame.setVisible(true);
	}
	
	public void drawGraph() {	
		nodeData = new ArrayList<NodeData>();
		HashSet<Edge> edges = graph.getEdges();
		HashSet<Node> nodes = graph.getNodes();
		
		drawNodes(nodes);
		drawEdges(edges);
		repaint();
		
	}
	
	public void addEdge(String start, String end) {
		graph.insertEdge(start, end);
		drawGraph();
	}
	
	private void drawNodes(HashSet<Node> nodes) {
		Iterator<Node> i = nodes.iterator();
		while (i.hasNext()) {
			drawNode(i.next());
		}
	}
	
	private void drawEdges(HashSet<Edge> edges) {
		Iterator<Edge> i = edges.iterator();
		Edge tmpEdge;
		while (i.hasNext()) {
			tmpEdge = i.next();
			drawEdge(tmpEdge);
		}
	}
	
	private void drawEdge(Edge edge) {
		Graphics g = contentPane.getGraphics();
		NodeData predecessor = null;
		NodeData successor = null;
		
		for (int i=0; i<nodeData.size(); i++) {
			if (nodeData.get(i).getNode().equals(edge.getPredecessor())) {
				predecessor = nodeData.get(i);
			}
			if (nodeData.get(i).getNode().equals(edge.getSuccessor())) {
				successor = nodeData.get(i);
			}
		}

		g.drawLine(predecessor.getX() + 15, predecessor.getY() +15, successor.getX() + 15, successor.getY() + 15);
	}
	
	private void drawNode(Node node) {
		Graphics g = contentPane.getGraphics();
		g.setColor(Color.green);	//Knoten
		g.fillOval(currentX, currentY, 50, 50);
		g.setColor(Color.black);	//Beschriftung der Knoten
		g.setFont(new Font("Serif",Font.BOLD,30));
		g.drawString(new String(node.getValue()).toString(), currentX + 15, currentY + 36); 
		nodeData.add(new NodeData(node, currentX, currentY));
		currentX += stepX; // muss geändert werden!
		currentY += stepY; 	
	}
	
	private boolean isGraphNameOk(String name) {
		if (name.length() > 2 || name.equals("")) {
			return false;
		} 
		else {
		return true;
		}
	}
	
	public void startAddEdgeView() {
		AddEdgeView addEdgeView = new AddEdgeView(this);
		addEdgeView.setVisible(true);
		repaint();
	}
	
	public void paint(Graphics a) { 
		//drawGraph();
		//repaint();
	}
}



//Hier die Klassse NodeData:

package application.gui;

import objects.Node;

public class NodeData {
	private Node node;
	private int x;
	private int y;
	
	public NodeData(Node node, int x, int y) {
		this.node = node;
		this.x = x;
		this.y = y;
	}
	
	public Node getNode() {
		return node;
	}
	
	public int getX() {
		return x;
	}
	
	public int getY() {
		return y;
	}
}

Mit der paint()-Methode klappt auch nicht alles so, wie ich es mir gedacht habe, aber das ist erstmal egal 😉
Wichtig ist zunächst die richtige Anordnung der Knoten, die momentan einfach nur mit jeweils etwas Abstand dazwischen ausgegeben werden (stepX, stepY).
Wenn es mit dem kreisförmigen zu schwierig ist, geht auch eine andere Art der Anordnung, mir geht's erstmal nur darum, dass sich die Kanten nicht überlagern und man erkennt, welcher Knoten mit welchem verbunden ist 🙂
 
Wenn es mit dem kreisförmigen zu schwierig ist, geht auch eine andere Art der Anordnung

Die Frage ist: ist es dir zu schwierig?
Ausgangspunkt: Menge an Knoten und Kanten. Ausmaß eines einzelnen Knoten in der Darstellung

Du brauchst ja nun nur einen Kreisumfang berechnen welcher Platz für alle Darstellungen hat. Mit dem Umfang kannste dann den Radius des Kreises bestimmen. Nun brauchste nur deine ganzen Knoten auf 360° aufteilen, in Abhängigkeit des Radius' ne Position bestimmt eh voilà. Kanten von Knotenmittelpunkt zu Mittelpunkt (oder erst beginnend ab Darstellungsgrenzbereich der Knoten).

So in etwa stelle ich mir das vor, um das Kreislayout umzusetzen
 
grundsätzlich macht man sowas nicht...
Java:
    private void drawNode(Node node) {
        Graphics g = contentPane.getGraphics();

man würde eher die
Code:
paintComponent(Graphics g){
überschreiben und dann deine Methoden aufrufen.
Idealerweise in einem JPanel, oder einer anderne Component...nicht die des JFrames.

also so:

Java:
@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		drawEdge(edge, g);//usw
		
	}
	  private void drawEdge(Edge edge, Graphics g) {
	        NodeData predecessor = null;
 

Zurück
Oben