1

リンクがノードを指す矢印である強制指向レイアウトを実行しようとしています (ここここに示されている例のように)。また、子を持つノードが折りたたみ可能です (Mike Bostock の例に示されているように:ここ またはここで)。

これまでのところ、ノードの折りたたみは正常に機能していますが、パスに矢印の頭がどのように含まれているかを理解するのに苦労しています。上記の例に基づいたコードの一部を次に示します。

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)");)。

しかし、グラフにはパスが表示されていますが、矢印は表示されていないため、何かが欠けている可能性があります。

助けてくれてありがとう!

4

1 に答える 1

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;
    }

矢印が表示されなかった理由の 1 つは、 のようにパスにクラスを指定したためだと思いますがpath.enter().insert("svg:path").attr("class", "link")、パスが選択されているときにそれを正しく参照していなかったため、パスが描画されませんでした
(つまり、私が持っていた:

  var path = vis.selectAll("path")  

本来あるべき場所:

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

次に、ノードを折りたたんだり展開したりするときに呼び出される update() 関数の外側で、矢頭マーカーの定義を定義する必要があることもわかりました。そうしないと、ノードをクリックするたびに svg に追加されますが、これはあまり効果的ではありません。

そのため、ノードは onclick で折りたたまれ、矢印が描画されます (見苦しいですが)。ただし、非常に混乱する問題が 1 つあります。しばらくすると、明らかな時間間隔やクリック パターンがないと、グラフがフリーズし、ブラウザでのデバッグでCannot read property 'target' of undefined. このエラーは、曲線パスが定義されている "tick" 関数でトリガーされます。

    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() ...

奇妙なことに、最初は機能していても、しばらくすると突然機能しなくなることがあります。それで、何が起こっているのかについて何らかの考えを持っている体はありますか??

編集:

私は今、何が間違っていたのかを知っています。何らかの理由で、どこかで見つけたスクリプト d3.layout.js を使用していました。これは、ツリーの折りたたみに必要だと思いました。私はそのライブラリを削除し、通常の d3.v3.js を使用しました。すべてが正常に機能します...矢印が醜いだけです。したがって、折りたたみ可能なノードと有向パスを使用して、上記のスクリプトが機能するはずです。

于 2013-05-06T19:58:37.990 に答える