17

D3 は初めてで、インタラクティブなネットワーク ビジュアライゼーションを作成しようとしています。この例の大部分をコピーしましたが、「パス」ではなく SVG の「線」を使用して曲線を直線に変更し、ノードが表すデータに従ってノードをスケーリングしました。問題は、私の矢印 (SVG マーカーで作成) が行末にあることです。一部のノードは大きいため、矢印はそれらの後ろに隠れます。矢印が指しているノードの外側の端に矢印が表示されるようにしたいと思います。

マーカーとリンクを作成する方法は次のとおりです。

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

var link = svg.selectAll(".link")
    .data(force.links())
    .enter().append("line")
    .attr("class", "link")
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

「refX」属性が、行末から矢印を表示する距離を指定していることに気付きました。これを指しているノードの半径に依存させるにはどうすればよいですか? それができない場合は、代わりに線自体の端点を変更できますか? すべてが移動するときに線の端点をリセットするこの関数でそれを行うと思います:

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

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

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

どちらのアプローチがより理にかなっており、どのように実装すればよいでしょうか?

4

2 に答える 2

18

Lars Kotthoffに感謝します。他の質問からのアドバイスに従って、これを機能させました! まず、線の使用からパスの使用に切り替えました。実際にそうする必要はなかったと思いますが、パスを使用していたので、私が見ていた他の例を簡単にたどることができました。

次に、ノードに「radius」フィールドを追加しました。値をすぐに返すのではなく、実際のフィールドとして追加することで、radius 属性を設定したときにこれを行いました。

var circle = svg.append("svg:g").selectAll("circle")
                    .data(force.nodes())
                    .enter().append("svg:circle")
                    .attr("r", function(d) {
                        if (d.logic != null) {
                            d.radius = 5;
                        } else {
                            d.radius = node_scale(d.classSize);
                        }
                        return d.radius;

次に、 tick() 関数を編集して、この半径を考慮しました。これには、少し単純なジオメトリが必要でした...

function tick(e) {

        path.attr("d", function(d) {
            // Total difference in x and y from source to target
            diffX = d.target.x - d.source.x;
            diffY = d.target.y - d.source.y;

            // Length of path from center of source node to center of target node
            pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY));

            // x and y distances from center to outside edge of target node
            offsetX = (diffX * d.target.radius) / pathLength;
            offsetY = (diffY * d.target.radius) / pathLength;

            return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY);
        });

基本的に、パスによって形成される三角形は、x の変化の合計 (diffX) と y の変化の合計 (diffY) であり、ターゲット ノード内のパスのセグメント (つまり、ノードの半径) によって形成される三角形と同様の三角形です。 x はターゲット ノード内で変化し (offsetX)、y はターゲット ノード内で変化します (offsetY)。これは、パスの合計長に対するターゲット ノードの半径の比率が、offsetX と diffX の比率、および offsetY と diffY の比率に等しいことを意味します。

refXまた、矢印の値を 10 に変更しました。なぜそれが必要だったのかはわかりませんが、今ではうまくいくようです!

于 2013-05-25T21:29:19.337 に答える