0

I have a working D3 example. I use the force and everything works fine. But I have a small issue. Let's say I have 4 Nodes. Just see this picture: https://i.stack.imgur.com/HwniH.png and now when I click on the Node "AKZO NV" then I want to get: https://i.stack.imgur.com/85oBp.png with the old nodes and links.

So at the end I want to have 7 Nodes. And the "AKZO NV" shall be focused. All Nodes still have their links and the "AKZO NV" shall have two. I think you now know what I want.

So I already have this code. Working like a charm but it's not properly adding the new nodes and links. I think there is a small issue with the order of the commands.

Any ideas are welcome:

var alreadyThere = false;
var nodeCircles = {};
var svg, link, node;
var force = d3.layout.force();
var nodes, links;
var width = 700, height = 400;
var boxIDName = "#main-rightinfo";
var JSONFORMERGING;

function createRealGraph(jsonData){
    //console.log(jsonData);
    if (alreadyThere == false){
        JSONFORMERGING=jsonData;
        initializeGraph(jsonData);
    }else{
        update(JSONFORMERGING.concat(jsonData));
    }
    alreadyThere = true;
}
function update(jsonData) {
    console.log(jsonData);
    jsonData.forEach(function(link) {
      link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, ID: link.source, class: link.sourceClass});
      link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, ID: link.target, class: link.targetClass});
    });

    link = link.data(links);
    link.enter().insert("line")
      .attr("class", "link");

    node = node.data(nodes);
    node.enter().append("g")
        .attr("class", "node")
      .attr("r", 5)
      .call(force.drag);

    force 
        .nodes(d3.values(nodeCircles))
        .links(jsonData)
        .start();
    nodes = force.nodes();
    links = force.links();
}
function initializeGraph(jsonData){
    jsonData.forEach(function(link) {
      link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, ID: link.source, class: link.sourceClass});
      link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, ID: link.target, class: link.targetClass});
    });

    force 
        .nodes(d3.values(nodeCircles))
        .links(jsonData)
        .size([width, height])
        .linkDistance(60)
        .charge(-200)
        .on("tick", tick)
        .start();

    nodes = force.nodes();
    links = force.links();

    svg = d3.select("#main-right")
        .append("svg")
        .attr("width", width)
        .attr("height", height);
    svg
        .append("svg:defs").selectAll("marker")
        .data(["end"]) 
        .enter().append("svg:marker")   
        .attr("id", String)
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 27)
        .attr("refY", -0.5)
        .attr("markerWidth", 6)
        .attr("markerHeight", 6)
        .attr("orient", "auto")
        .append("svg:path")
        .attr("d", "M0,-5L10,0L0,5")
        .attr('fill', '#00b');

    link = svg.selectAll(".link")
        .data(links)
        .enter().append("line")
        .attr("class", "link")
        .attr("marker-end", "url(#end)");

    node = svg.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", "node")
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", function(d) {click(d);})
        .on("dblclick", function(d) {dblclick(d);})
        .call(force.drag);
    node
        .append("image")
        .attr("xlink:href",  function(d) {if (d.class == "Person") {return "pics/node_person.png";} else {return "pics/node_appln.png";} })
        .attr("x", -20)
        .attr("y", -20)
        .attr("width", 40)
        .attr("height", 40);
    node
        .append("text")
        .attr("x", 19)
        .attr("dy", ".25em")
        .text( function(d) {return d.name; });

    function tick() {
      link
          .attr("x1", function(d) { return d.source.x; })
          .attr("y1", function(d) { return d.source.y; })
          .attr("x2", function(d) { return d.target.x; })
          .attr("y2", function(d) { return d.target.y; });
      node
          .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    }
}
kwoxer
  • 3,734
  • 4
  • 40
  • 70
  • Not sure if I understand what you want. Your two pictures show the same number of nodes -- where do you want to add new ones and how? – Lars Kotthoff Sep 25 '14 at 17:30
  • Well you are right, the number of nodes are the same. But as I said I want to add the second picture to the first one. In the first a Document is the source. But in the first its a Person who is the focus. But I don't want to loose the like to the Document there for example. Well if it's still unclear just say. And I do another picture. – kwoxer Sep 25 '14 at 17:42
  • Still don't know what you want -- duplicate all the nodes? – Lars Kotthoff Sep 25 '14 at 17:52
  • I'll do a picture =) – kwoxer Sep 25 '14 at 18:02
  • Here you go, I need something like this http://i.imgur.com/rMFcrre.png – kwoxer Sep 25 '14 at 18:10
  • Have you seen [this question](http://stackoverflow.com/questions/9539294/adding-new-nodes-to-force-directed-layout)? – Lars Kotthoff Sep 25 '14 at 19:22
  • Yes, but does not really help in my case. – kwoxer Sep 25 '14 at 20:11
  • Ahh I have it, but it's ugly code. Will work on the code post it in 2 days as answer. Till then please give me ideas if you have some. =) – kwoxer Sep 25 '14 at 21:04

2 Answers2

1

You just need to test if it's NOT an object. That works like a charm:

jsonData.forEach(function(link) {
        if (typeof(link.source) != "object"){
            link.source = nodeCircles[link.source] || (nodeCircles[link.source] = {name: link.sourceName, ID: link.source, class: link.sourceClass});
        }
        if (typeof(link.target) != "object"){
            link.target = nodeCircles[link.target] || (nodeCircles[link.target] = {name: link.targetName, ID: link.target, class: link.targetClass});
        } 
    });   
kwoxer
  • 3,734
  • 4
  • 40
  • 70
0

I'm working on the same problem and got a "solution". It is still a prototype, but maybe it helps you.

Every mouse click on a node calls a function and detects new nodes. They are all saved in nodes and all active, it means all nodes on the display are in activeNodes. All nodes belonging to the path from the root node to the clicked one are stored in pathNodes.The result is, that only the active path is displayed inclusivly the children of the clicked one.

Hopefully it is clearly explained. For abetter understanding please look up the source code at: http://github.com/nextlevelshit/d3_nested_nodes

For something to play with check out: http://dailysh.it/github/d3_nested_nodes/

Here the snippet of my code:

/**
 * Triggering mouse click start
 */

function mousedown() {

  if (mousedown_node !== null) {

    var pathNodes = findPathNodesTo(mousedown_node);

    var point = d3.mouse(this),
      node = {
        id: nodes.length,
        parent: mousedown_node.id
      };
    node.x = point[0];
    node.y = point[1];

    var newNodes = findNodesbyParentId(mousedown_node.id),
      startingPoint = {
        x: mousedown_node.x,
        y: mousedown_node.y
      };

    for (var i = 0; i < pathNodes.length; i++) {
      newNodes.push(pathNodes[i]);
      pathNodes[i].path = true;
    }

    var removeNodes = activeNodes.diff(newNodes);
    var addNodes = newNodes.diff(pathNodes).diff(activeNodes);

    for (var i = 0; i < removeNodes.length; i++) {
      removeNode(removeNodes[i].id);
    }

    for (var i = 0; i < addNodes.length; i++) {
        addNodes[i].x = startingPoint.x;
        addNodes[i].y = startingPoint.y;
        activeNodes.push(addNodes[i]);
        activeLinks.push({source: findNode(addNodes[i].parent), target: findNode(addNodes[i].id)});
    }

      // TODO: Find a smoother way do delay popping out new nodes
    });

  }

  restart();
}

The last problem is to find a smooth way for popping out new nodes...

Michael W. Czechowski
  • 3,366
  • 2
  • 23
  • 50