# Einfacher Baum visualisieren.



## 23 (14. Jan 2011)

Hallo,

Die Daten liegen als einfacher Baum vor, z.B. ein Personen Objekt mit einer Liste als Membervariable, die  weitere Personen Objekt beinhaltet.

Das ganze sollte ungefähr so aussehen:

http://www.yworks.com/img/net40/net_collapsible_tree.png 

Wie implementiere ich so etwas? Hat jemand ein SampleCode?

Viele Grüße


----------



## Wildcard (14. Jan 2011)

So zB:
Zest


----------



## Marco13 (14. Jan 2011)

Womit nochmal der Unterschied deutlich geworden wäre, zwischen "einen einfachen Baum visualisieren" und "einen Baum einfach visualisieren". Vielleicht geht das ja mit "yFiles"?  Mal im ernst: Es gibt Bibliotheken für sowas, JGraph, Jung & Co, die für allgemeine Graphen gedacht sind. Wenn es NUR um einen speziellen, kleinen Baum geht, und nicht 50 verschiedene Renderer, Färbungen, Interaktionen und LayoutManager verwendet werden sollen, könnte die Einarbeitung in so eine Lib aufwändiger sein, als es selbst schnell irgendwie hinzuschreiben.


----------



## Wildcard (14. Jan 2011)

> Mal im ernst: Es gibt Bibliotheken für sowas, JGraph, Jung & Co, die für allgemeine Graphen gedacht sind. Wenn es NUR um einen speziellen, kleinen Baum geht, und nicht 50 verschiedene Renderer, Färbungen, Interaktionen und LayoutManager verwendet werden sollen, könnte die Einarbeitung in so eine Lib aufwändiger sein, als es selbst schnell irgendwie hinzuschreiben.


Deshalb habe ich ja Zest vorgeschlagen. Es sind Snippets dabei und wenn man nicht viel customizen will, sondern nur Knoten visualisieren und layouten möchte, dann ist man für solche Graphen bei Zest mit 20-50 Zeilen Code dabei.


----------



## André Uhres (15. Jan 2011)

23 hat gesagt.:


> Wie implementiere ich so etwas? Hat jemand ein SampleCode?



Hallo 23,

hier ist ein kleines Beispiel:


```
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class TreeDemo extends JFrame {
    public TreeDemo() {
        super("TreeDemo");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(400, 400);
        setLocationRelativeTo(null);
        Tree tree = createDemoTree();
        Node root = tree.getRoot();
        root.moveTo(getSize().width / 2 - root.getBounds().width / 2, 20);
        root.balanceX();
        root.balanceY();
        add(new TreeComponent(tree));
    }
    public static void main(final String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TreeDemo().setVisible(true);
            }
        });
    }
    private Tree createDemoTree() {
        Node root = new Node(new Person("Peter"));
        Node n2 = new Node(new Person("Andrew"));
        Node n3 = new Node(new Person("Paul"));
        Node n5 = new Node(new Person("Jane"));
        Node n8 = new Node(new Person("Bert"));
        Node n9 = new Node(new Person("Big Blue"));
        n9.setBounds(new Rectangle(0, 0, 100, 100));
        Node n10 = new Node(new Person("Benny"));
        root.addChild(n3);
        root.addChild(n9);
        n3.addChild(n2);
        n3.addChild(n5);
        n9.addChild(n8);
        n9.addChild(n10);
        return new Tree(root);
    }
}
class TreeComponent extends JPanel {
    private Tree tree;
    public TreeComponent(final Tree tree) {
        this.tree = tree;
    }
    @Override
    public void paintComponent(final Graphics g) {
        drawTree(g, tree.getRoot());
    }
    private void drawTree(final Graphics g, final Node node) {
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        drawNode(g, node);
        int numberOfChildren = node.numberOfChildren();
        for (int i = 0; i < numberOfChildren; i++) {
            drawTree(g, node.childNr(i));
        }
    }
    private void drawNode(final Graphics g, final Node node) {
        g.setColor(Color.BLACK);
        g.drawRect(node.getBounds().x, node.getBounds().y,
                node.getBounds().width, node.getBounds().height);
        FontMetrics fm = getFontMetrics(getFont());
        int i = fm.stringWidth(node.getElement().toString());
        g.drawString(node.getElement().toString(), (node.getBounds().x
                + node.getBounds().width / 2) - (i / 2), node.getBounds().y
                + node.getBounds().height / 2);
        g.setColor(Color.BLUE);
        for (int j = 0; j < node.numberOfChildren(); j++) {
            Node child = node.childNr(j);
            g.drawLine(node.getBounds().x
                    + node.getBounds().width / 2, node.getBounds().y
                    + node.getBounds().height,
                    child.getBounds().x
                    + child.getBounds().width / 2, child.getBounds().y);
        }
    }
}

class Node{
    private Object data;
    private List<Node> children;
    private Rectangle bounds;

    public Node(final Object o) {
        data = o;
        children = new ArrayList();
        bounds = new Rectangle(0, 0, 50, 50);
    }
    public void addChild(final Node child) {
        children.add(child);
    }
    public Object getElement() {
        return data;
    }
    //
    public Rectangle getBounds() {
        return bounds;
    }
    public void setBounds(final Rectangle bounds) {
        this.bounds = bounds;
    }
    public Node childNr(final int nr) {
        return children.get(nr);
    }
    public int numberOfChildren() {
        return children.size();
    }
    private int childrenWidth() {
        int width = 0;
        for (int i = 0; i < numberOfChildren(); i++) {
            width += childNr(i).maxWidth();
            if (i != numberOfChildren() - 1) {
                width += 10;
            }
        }
        return width;
    }
    private int maxWidth() {
        int cWidth = childrenWidth();
        if (cWidth > getBounds().width) {
            return cWidth;
        } else {
            return getBounds().width;
        }
    }
    public void balanceX() {
        if (numberOfChildren() > 0) {
            int cWidth = childrenWidth();
            int middleX = getBounds().x + getBounds().width / 2;
            int startX = middleX - cWidth / 2;
            for (int i = 0; i < numberOfChildren(); i++) {
                Node child = childNr(i);
                int maxWidth = child.maxWidth();
                int newX = (startX + maxWidth / 2) - child.getBounds().width / 2;
                child.moveTo(newX, child.getBounds().y);
                child.balanceX();
                startX = startX + maxWidth + 10;
            }
        }
    }
    private void balanceY(final int height) {
        int childHeight = 0;
        int newY = getBounds().y + height + 20;
        for (int i = 0; i < numberOfChildren(); i++) {
            Node child = childNr(i);
            if (child.getBounds().height > childHeight) {
                childHeight = child.getBounds().height;
            }
        }
        for (int i = 0; i < numberOfChildren(); i++) {
            Node child = childNr(i);
            child.moveTo(child.getBounds().x, newY);
            child.balanceY(childHeight);
        }
    }
    public void balanceY() {
        balanceY(getBounds().height);
    }
    public void moveTo(final int newX, final int newY) {
        getBounds().x = newX;
        getBounds().y = newY;
    }
}

class Tree {
    private Node root;
    public Tree(final Node root) {
        this.root = root;
    }
    public Node getRoot() {
        return root;
    }
}
class Person{
    private final String name;
    public Person(final String name){
        this.name = name;
    }
    @Override
    public String toString(){
        return name;
    }
}
```

Gruß,
André


----------



## 23 (15. Jan 2011)

Vielen Dank ich werde, sobald ich in der Lage bin mir alles anschauen 

ICh brauche wirklich nur eine kleine Idee wie ich so etwas implementieren kann. Der spätere Code wird nicht einmal mit Java geschrieben... trotzdem wollte ich ein einfaches Java Beispiel da ich wußte hier sind einige gute Hacker ;-)

Viele Grüße


----------



## Marco13 (15. Jan 2011)

Wildcard hat gesagt.:


> Deshalb habe ich ja Zest vorgeschlagen. Es sind Snippets dabei und wenn man nicht viel customizen will, sondern nur Knoten visualisieren und layouten möchte, dann ist man für solche Graphen bei Zest mit 20-50 Zeilen Code dabei.



Falls das mißverständlich war: Den Beitrag hatte ich geschrieben, ohne den Hinweis auf Zest gesehen zu haben. Das hattest du schon ein paar mal für solche Sachen empfohlen, aber ich hatte es hier dann vergessen. Es sieht auf Basis der Beispiele wirklich einfach aus, muss ich mir unbedingt mal :rtfm:en


----------



## 23 (17. Jan 2011)

André Uhres vielen Dank genau so etwas habe ich gebraucht.

Ich konnte aus deinem Sample endlich mal sehen wie man da überhaupt vorgehen kann 

Wie lange hast du dafür gebraucht und war dies Teil deines Studiums/Ausbildung?

Hat diese Implementierung einen Namen bzw. auf wen geht diese evtl. zurück?

Ist dies der beste Weg? Gibt es noch effizientere Implementierungen?

Ich würde mich auch sehr freuen falls jemand einige seiner FH/Uni Unterlagen und Code bereitstellen würde... wir hatten zu diesem Thema leider nur eine Vorlesung und da ging es nur um Bäume ohne das Zeichnen :-(


----------



## André Uhres (17. Jan 2011)

Hallo 23,

 ich hatte vor Jahren ein entsprechendes Beispiel im Netz gefunden. Das finde ich aber jetzt nicht wieder. Viel Besonderes ist ja eigentlich auch nicht dabei, außer dass ein paar Methoden rekursiv arbeiten, wie "drawTree" und die "balance" Methoden.

 Das Prinzip habe ich inzwischen in anderen Projekten verwendet und immer wieder angepasst. Von daher weiß ich auch nicht mehr, wie lange ich dafür gebraucht habe. Man kann es als Teil meines Selbst-Studiums ansehen (kein Hochschulstudium).

 Ob es einen besseren oder effizienteren Weg gibt, weiß ich nicht. Ich glaube jedoch, dass die Implementierung ziemlich klar strukturiert und relativ gut verständlich ist. Ich bin auch kein Freund von größerer Effizienz auf Kosten der Klarheit, besonders wenn die "größerer Effizienz" kaum wahrnehmbar oder messbar ist.

 Gruß,
 André


----------



## 23 (14. Apr 2011)

Wie würde ihr bei diesem Code eine gesamte Berechnung der Breite und Höhe des Baums einpflegen?

Neue Methoden schreiben?


----------



## André Uhres (14. Apr 2011)

23 hat gesagt.:


> Wie würde ihr bei diesem Code eine gesamte Berechnung der Breite und Höhe des Baums einpflegen?



Da das Layout durch die "balance.." Methoden ausgeführt wird, würde die Berechnung der Größe wohl am besten auch dorthin passen .

Gruß,
André


----------



## André Uhres (18. Apr 2011)

Hallo 23,

nach eingehender Überlegung ist es meiner Meinung nach besser, die Funktion einfach in eine neue Methode auszulagern. Bei der folgenden Lösung gibt die Methode [c]getBounds(final int BORDER)[/c] in der Klasse [c]Node[/c] die Größe des Knotens (samt aller Unterknoten) zurück, unter Berücksichtugung eines eventuell notwendigen Randes. Die Methode ruft [c]calcSize[/c] auf, eine rekursive Methode, die einfach nur den Teilbaum durchläuft, um den oberen rechten und den unteren linken Punkt einer imaginären "Boundingbox" zu ermitteln:


```
...
public class TreeDemo extends JFrame {
    public TreeDemo() {
...
        Rectangle bounds = root.getBounds(10);
        System.out.println("bounds = " + bounds);
    }
...
}
...
class Node{
...
    private Point upperLeftCorner = new Point(0, 0);
    private Point lowerRightCorner = new Point(0, 0);
...
    private void calcSize(final Node node) {
        int numberOfChildren = node.numberOfChildren();
        for (int i = 0; i < numberOfChildren; i++) {
            Node child = node.childNr(i);
            if(child.numberOfChildren() > 0){
                calcSize(child);
            }else{
                int x1 = child.getBounds().x;
                int x2 = x1 + child.getBounds().width;
                int y1 = child.getBounds().y;
                int y2 = y1 + child.getBounds().height;
                if (x1 < upperLeftCorner.x) {
                    upperLeftCorner.x = x1;
                }
                if (x2 > lowerRightCorner.x) {
                    lowerRightCorner.x = x2;
                }
                if (y1 < upperLeftCorner.y) {
                    upperLeftCorner.y = y1;
                }
                if (y2 > lowerRightCorner.y) {
                    lowerRightCorner.y = y2;
                }
            }
        }
    }
    public Rectangle getBounds(final int BORDER) {
        upperLeftCorner.x = getBounds().x;
        upperLeftCorner.y = getBounds().y;
        lowerRightCorner.x = getBounds().x + getBounds().width;
        lowerRightCorner.y = getBounds().y + getBounds().height;
        calcSize(this);
        upperLeftCorner.x -= BORDER;
        upperLeftCorner.y -= BORDER;
        lowerRightCorner.x += BORDER;
        lowerRightCorner.y += BORDER;
        return new Rectangle(upperLeftCorner.x, upperLeftCorner.y, lowerRightCorner.x - upperLeftCorner.x, lowerRightCorner.y - upperLeftCorner.y);
    }
...
```

Gruß,
André


----------

