Tree Visualization - Organizing Parents/Children

Right now I am having trouble getting the parent/child relationship working in my tree visualization. The problem comes where I am trying to get children to display, the childrens children to display, and make sure that all can be expanded and collapsed appropriately. (Should be able to expand node, expand it's child and their children, then be able to collapse each of those). I am getting lost in the logic, does anybody have some tips?

Main Sketch:

Node root;
Node current;
XML data;

void setup(){
    root = new Node();
    data = loadXML("nodes.xml");

    size(750,750);
    smooth(true);
    background(255);

    loadChild(root, data, null);
}

void loadChild(Node node, XML data, Node parent) {
  /* LoadChild() pulls in XML, assigns titles, assigns types,
     creates new nodes for children, and repeats until all nodes
     are created.                                                */

  //------------------Title & Type-----------------------//
    XML title = data.getChild("title");
    XML type = data.getChild("type");
    String xmlTitle = title.getContent();
    String xmlType = type.getContent();
    node.title = xmlTitle;
    node.type = xmlType;

  //----------------Create children----------------------//
    XML[] XMLchildren = data.getChildren("child");
    node.children = new Node[XMLchildren.length];

    for(int i=0;i<XMLchildren.length;i++){
      node.children[i] = new Node();
    }

  //---------------------DEBUG---------------------------//
    println("Current Node: " + node.title);   
    if(parent!=null){
      println("Parent: " + parent.title);
      node.parent = parent;
    }
    if(node.children!=null){
        println("Number of Children:" + XMLchildren.length);
    }
    if(node.type!=null){
        println("Node Type: " + node.type);
    }
    println("");

  //---------------------REPEAT--------------------------//
    for (int i = 0; i < XMLchildren.length; i++) {
      loadChild(node.children[i], XMLchildren[i], node);
    }
}

void draw(){
  //--------------------INITIALIZE-----------------------//
    background(255);
    root.display();

  //-----------------DISPLAY CHILDREN--------------------//
    if(root.collapsed == false) {                        // FIRST RUN FROM NODE
      for (int i = 0; i < root.children.length; i++) {
        root.children[i].display();                      // FIRST TIER CHILDREN
      }
    }
}

void mousePressed(){
  //--------------------INITIALIZE-----------------------//
    root.clickedActive();

  //---------------------CHILDREN------------------------//  
    for (int i = 0; i < current.children.length; i++) {
      root.children[i].clickedActive();
    }
}

Node Class:

/* @pjs font="Junction-light.otf"; */
PFont f;

class Node {

    Node parent;
    Node[] children;
    public String title;
    public String type;

    int x=80;
    int y=140;
    int tWidth;
    int tHeight;
    int titleColor = 0;
    float textSize=70;
    public boolean active = false;
    public boolean collapsed = true;

    Node(){
      f = createFont("Junction-light.otf",100,true);
      textFont(f,textSize);  
    }

    void display(){
      /* Display() prints text, with the link color and
         bounding box debug. */

      if(parent == null) {
        fill(titleColor);
        text(title,x,y);

        //-------------------DEBUG----------------------//
        textFont(f,textSize/3);
        if(active==true){text("Active",x*7,y);}
          else if(active==false){text("Inactive",x*7,y);}
          else{text("Undefined",x*5,y);}
        if(collapsed==true){text("Collapsed",x*7,y-30);}
          else if(collapsed==false){text("Expanded",x*7,y-30);}
          else{text("Undefined",x*5,y-30);}
        //----------------------------------------------//

        textFont(f,textSize);  
        noFill();
      } else if(parent.collapsed == true){
        noFill();
        noStroke();
        text(title,x,y);
      }else{
        fill(titleColor);
        text(title,x,y);

        //-------------------DEBUG----------------------//
        textFont(f,textSize/3);
        if(active==true){text("Active",x*7,y);}
          else if(active==false){text("Inactive",x*7,y);}
          else{text("Undefined",x*5,y);}
        if(collapsed==true){text("Collapsed",x*7,y-30);}
          else if(collapsed==false){text("Expanded",x*7,y-30);}
          else{text("Undefined",x*5,y-30);}
        //----------------------------------------------//

        textFont(f,textSize);  
        noFill();
      }

      tHeight = textAscent();
      tWidth  = textWidth(title)+8;
      //rect(x,y-tHeight,tWidth,tHeight);
    }

    void displaySpecialTypeA(){
      println("specialTypeA");
    }

    void displaySpecialTypeB(){
      println("specialTypeB");
    }

    void displaySpecialTypeC(){
      println("specialTypeC");
    }

    void clickedActive(){
      /* First checks if click is within bounds. If in bounds, and is
         not already active, set active and expand, else, set inactive
         and collapse. */
      if(mouseX > x && mouseX < x+tWidth && mouseY > y-tHeight && mouseY < y){
        if(active == false){      //if already inactive (default)
          titleColor = 140;       //make gray
          active = true;          //set active
          current = this;         //set node as current
          collapsed = false;      //
          expandChildren();       //
        }else if(active == true){ //if already active
          titleColor = 0;         //make black
          active = false;         //set inactive
          collapsed = true;       //
          collapseChildren();     //
        }
      }
    }

    void expandChildren(){
      /* Expands all immediate children nodes. */
      /* TEMPORARY: Distributes children. */
      for (int i = 0; i < children.length; i++) {
        //this.children[i].collapsed = false;
        this.children[i].y = y + 150 + 150*i;
      }
    }

    void collapseChildren(){
      /* Collapses all immediate children nodes and makes inactive. */
      for (int i = 0; i < children.length; i++) {
        //this.children[i].collapsed = true;
        this.children[i].active = false;
      }
    }

}

nodes.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>

    <type></type>
    <title>Modernism</title>
    <description></description>
    <image></image>

    <child>
        <type></type>
        <title>Authors</title>
        <description></description>
        <image></image>

        <child>
            <type></type>
            <title>Franz Kafka</title>
            <description></description>
            <image></image>
        </child>

        <child>
            <type></type>
            <title>Gertrude Stein</title>
            <description></description>
            <image></image>
        </child>

        <child>
            <type></type>
            <title>T.S. Eliot</title>
            <description></description>
            <image></image>
        </child>
    </child>

    <child>
        <type></type>
        <title>History</title>
        <description></description>
        <image></image>

        <child>
            <type></type>
            <title>Paris</title>
            <description></description>
            <image></image>
        </child>
    </child>

    <child>
        <type></type>
        <title>Resources</title>
        <description></description>
        <image></image>

        <child>
            <type></type>
            <title>Google it.</title>
            <description></description>
            <image></image>
        </child>
    </child>

</root>

Thanks

Tagged:

Answers

  • Are you working with a recursive structure to store the nodes?

  • I suppose I should be, this was just proposed to me from a friend:

    void mouseHandle(int depth, Node node){
      node.clickedActive();
      if(depth!=0){
        for(int i = 0; i < node.children.length; i++){
          mouseHandle(depth-1,node.children[i]);
        }
      }
    }
    

    to recursively handle the clickedActive() function. Is this along the right lines?

Sign In or Register to comment.