2013-05-05 3 views
1

Я пытаюсь сделать ориентированную по силе макет, в которой ссылки являются стрелками, указывающими на узлы (как в приведенных примерах here и here), а также в которых узлы, у которых есть дети, являются разборными (как показано в примерах Майка Бостока: here или here).D3js раскладная силовая компоновка с направленными дорожками?

До сих пор разрушение узлов прекрасно работает, но у меня возникают проблемы с пониманием того, как головки стрелок включены в пути. Вот часть моего кода, на основании приведенных выше примеров:

force.nodes(nodes) 
    .links(links) 
    .gravity(0.05) 
    .charge(-1500) 
    .linkDistance(100) 
    .friction(0.5) 
    .linkStrength(function(l, i) {return 1 }) 
    .size([w, h]) 
    .start(); 

// Append markers 
vis.append("svg:defs").selectAll("marker") 
    .data(["end"]) 
    .enter().append("svg:marker") 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("refY", -1.5) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
//.append("svg:path")  // <-- I not sure what this does 
    //.attr("d", "M0,-5L10,0L0,5"); 


var path = vis.selectAll("path") 
    .data(force.links()); 


// Enter new paths 
path.enter().insert("svg:path") 
    .attr("class", "link") 
    .attr("marker-end", "url(#end)") 
    .style("stroke", "#ccc"); 


// Exit any old paths. 
path.exit().remove(); 


// Update the nodes… 
var node = vis.selectAll("g.node") 
    .data(nodes, function(d) { return d.id; }) 

node.select("circle") 
    .style("fill", color); 

// Enter any new nodes. 
var nodeEnter = node.enter().append("svg:g") 
    .attr("class", "node") 
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 
    .on("click", click) 
    .call(force.drag); 


//Add an immage to the node 
nodeEnter.append("svg:image") 
     .attr("xlink:href", function(d) { return d.image;}) 
     .attr("x", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
     .attr("y", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
     .attr("height", 16) 
     .attr("width", 16); 


// Exit any old nodes. 
node.exit().remove(); 

// Re-select for update. 
node = vis.selectAll("g.node"); 
path = vis.selectAll("path") 

force.on("tick", function() { 
    // Draw curved links 
    path.attr("d", function(d) { 
    var dx = d.target.x - d.source.x, 
     dy = d.target.y - d.source.y, 
     dr = Math.sqrt(dx * dx + dy * dy); 
    return "M" + d.source.x + "," 
      + d.source.y 
      + "A" + dr + "," 
      + dr + " 0 0,1 " 
      + d.target.x + "," 
      + d.target.y; 
    }); 

    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); 
}); 

То, что я понимаю, что часть кода ниже отвечает за рисование стрел на, указав блок, где стрелки должны быть направлены на (например .data(["end"]))

vis.append("svg:defs").selectAll("marker") 
    .data(["end"]) 
    .enter().append("svg:marker") 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("refY", -1.5) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path")  
    .attr("d", "M0,-5L10,0L0,5"); 

Это затем сопоставляются, когда путь вводится (т.е. .attr("marker-end", "url(#end)");).

Но, возможно, мне что-то не хватает, потому что на моем графике показаны пути, но не стрелки.

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

+0

Хороший вопрос! Боже, я боролся с этими стрелками. Я с нетерпением жду, чтобы увидеть, что здесь происходит. – d3noob

+0

«Путь», который вы добавляете в определении маркера SVG, отвечает за рисование фактической стрелки. Вы запустили свой код с этим комментарием (первый блок кода)? –

+0

Спасибо @LarsKotthoff, я понял это сразу после того, как я разместил вопрос. Раньше это не срабатывало, когда оно было расколото, и поскольку я не понимал, что это такое, я прокомментировал это. Написание 'path.link' почти фиксировало его. Я поставил свой ответ ниже. Тем не менее, есть еще кое-что, что мешает ему полностью работать ... – djjupa

ответ

0

Я нашел решение, которое «почти» работает. Вот полный код и краткое объяснение того, что еще не удается в нижней части:

var w = 1280, 
     h = 800, 
     root, 
        vis; 

    var force = d3.layout.force() 
       .gravity(200) 
       .charge(-1500) 
       .linkDistance(100) 
       .friction(0.01) 
       .size([w, h]) 
       ; 


    $(document).ready(function() { 

     var newHeight = '100%'; 

     $("#svgdiv").html("<svg id='graph' xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'></svg>"); 

     vis = d3.select("svg"); 

     d3.json("../json/flare.json", function(json) { 
      root = json; 
      root.fixed = true; 
      root.x = w/2; 
      root.y = h/2; 


     // Build the arrow 
     var defs = vis.insert("svg:defs").selectAll("marker") 
      .data(["end"]); 

      defs.enter().append("svg:marker") 
      .attr("id", String) 
      .attr("viewBox", "0 -5 15 15") 
      .attr("refX", 15) 
      .attr("refY", -1.5) 
      .attr("markerWidth", 6) 
      .attr("markerHeight", 6) 
      .attr("orient", "auto") 
      .append("svg:path") 
      .attr("d", "M0,-5L10,0L0,5");  

      update(); 
     }); 
    }); 



    /** 
    * 
    */ 
    function update() { 
     var nodes = flatten(root), 
      links = d3.layout.tree().links(nodes); 

     // Restart the force layout. 
     force.nodes(nodes) 
      .links(links) 
      .gravity(0.05) 
      .charge(-1500) 
      .linkDistance(100) 
      .friction(0.5) 
      .linkStrength(function(l, i) {return 1 }) 
      .size([w, h]) 
      .start(); 


     var path = vis.selectAll("path.link") // <-- THIS WAS CHANGED TO "path.links" 
      .data(links, function(d) { return d.target.id; }); 

      path.enter().insert("svg:path") 
      .attr("class", "link") 
      .attr("marker-end", "url(#end)") 
      .style("stroke", "#ccc"); 


     // Exit any old paths. 
     path.exit().remove(); 


     // Update the nodes… 
     var node = vis.selectAll("g.node") 
      .data(nodes, function(d) { return d.id; }); 

     // Enter any new nodes. 
     var nodeEnter = node.enter().insert("svg:g") 
      .attr("class", "node") 
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 
      .on("click", click) 
      .call(force.drag); 


     node.select("circle") 
      .style("fill", color); 


     nodeEnter.append("svg:circle") 
      .attr("r", function(d) { return Math.sqrt(d.size)/10 || 4.5; }) 
      .style("fill", color); 

     // Add text to the node (as defined by the json file) 
     nodeEnter.append("svg:text") 
      .attr("text-anchor", "middle") 
      .attr("dx", function(d) { return Math.sqrt(d.size)/10 || 4.5; }) 
      .attr("dy", ".35em") 
      .text(function(d) { return d.name; }); 
     /* */ 

     //Add an image to the node 
     nodeEnter.append("svg:image") 
       .attr("xlink:href", function(d) { return d.logo;}) 
       .attr("x", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
       .attr("y", function(d) { return (0 - Math.sqrt(d.size))/10 || 4.5;}) 
       .attr("height", 16) 
       .attr("width", 16); 
     /* */  


     // Exit any old nodes. 
     node.exit().remove(); 

     // Re-select for update. 
     node = vis.selectAll("g.node"); 
     path = vis.selectAll("path.link"); // <-- THIS WAS CHANGED TO "path.link" 


     force.on("tick", function() { 


     path.attr("d", function(d) { 
      var dx = d.target.x - d.source.x, 
       dy = d.target.y - d.source.y, 
       dr = Math.sqrt(dx * dx + dy * dy); 
      return "M" + d.source.x + "," 
         + d.source.y 
         + "A" + dr + "," 
         + dr + " 0 0,1 " 
         + d.target.x + "," 
         + d.target.y; 
     }); 


     node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); 

     }); 

    } 


    // Color leaf nodes orange, and packages white or blue. 
    function color(d) { 
     return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; 
    } 

    // Toggle children on click. 
    function click(d) { 
     if (d.children) { 
     d._children = d.children; 
     d.children = null; 
     } else { 
     d.children = d._children; 
     d._children = null; 
     } 

     update(); 
    } 



    // Returns a list of all nodes under the root. 
    function flatten(root) { 
     var nodes = []; 
     var i = 0; 

     function recurse(node) { 
     if (node.children) 
      node.children.forEach(recurse); 
     if (!node.id) 
      node.id = ++i; 
     nodes.push(node); 
     } 

     recurse(root); 
     return nodes; 
    } 

Я думаю, что одна из причин того, что стрелки не показывали, что я дал класс на путь, как в path.enter().insert("svg:path").attr("class", "link"), но я не ссылаться на него правильно, когда был выбран путь, и поэтому он не нарисовал его
(т.е. у меня было:

var path = vis.selectAll("path") 

, где он должен был:

var path = vis.selectAll("path.link").) 

Затем я также узнал, что defs для маркеров стрелки должен быть определен вне функции update(), которая вызывается при свертывании и расширении узлов. В противном случае он присоединяет его к svg каждый раз, когда вы нажимаете на узел, и это не очень эффективно.

Итак, теперь узлы сжимаются и вырисовываются стрелки (хотя они уродливы). Тем не менее, есть еще одна проблема, которая меня очень смущает: через некоторое время, без какого-либо видимого временного интервала или шаблона щелчка, график зависает и отлаживается в браузере, говорит, что Cannot read property 'target' of undefined. Эта ошибка срабатывает от функции «тик», где криволинейная траектория определяется:

path.attr("d", function(d) { 
     var dx = d.target.x - d.source.x, 
      dy = d.target.y - d.source.y, 
      dr = Math.sqrt(dx * dx + dy * dy); 
     return "M" + d.source.x + "," 
        + d.source.y 
        + "A" + dr + "," 
        + dr + " 0 0,1 " 
        + d.target.x + "," 
        + d.target.y; 
    }); 

Он не находит d.target, поскольку переменная путь возобновлен (как этот path = vis.selectAll("path.link");) перед входом force.on("tick", function() ...

Странно то, что он работает в начале, и он внезапно может перестать работать после случайного времени! У любого органа есть представление о том, что может происходить?

EDIT:

теперь я знаю, что был неправ. По какой-то причине я использовал сценарий d3.layout.js, который я нашел где-то, что, по моему мнению, было необходимо для свертывания дерева.Я удалил эту библиотеку и использовал обычный d3.v3.js, и все работает так, как должно ... просто, что стрелки уродливы. Итак, сценарий выше должен работать, имея разборные узлы и направленные пути.

+0

Я создал суть, чтобы показать живой пример: [http://bl.ocks.org/djjupa/5653692](http://bl. ocks.org/djjupa/5653692) – djjupa