web-dev-qa-db-ja.com

フォースレイアウトの最初の目盛りを落ち着かせる

私はd3を少し始めたばかりで、学習曲線が非常に急であることがわかりました。プロセスは私が慣れているものとは完全に異なり、数学はほとんど私の頭の上にあります。

とにかく、私のプロジェクトは、システム間の統合マップを表すフォースレイアウトで構成されています。この部分は非常にうまく機能しますが、1つの大きな懸念があります。これは、Michael Bostocksサイトの強制指向レイアウトデモでも表されています。ノードが開始されると、キャンバスからレンダリングされるようです。この後、いくつかの深刻な物理学の数学が引き継いでおり、ノードをランダムな座標に落ち着いて落ち着くまでかなり混乱したパスでノードを前後に送る重力引きをシミュレートしています。これらの動きは、デモを初めて実行したときにクールになりますが、ネットワーク管理者の視点からネットワークインターフェースのステータスを表示しようとすると、サーバーが静止したままになりますが、しばらくすると疲れます。

サーバーを自動レイアウトしたいので、サーバー間のリンクを視覚化したいので、このプロジェクトの正しいレイアウト設定があると確信しています。しかし、重力効果に関しては私はあいまいです。

私は疑問に思う;各ノードの初期位置を手動で設定して、それらを重心に近づけて「バウンス時間」を少し短くすることは可能ですか?

47

内部的には、「通常の」使用法では、強制レイアウトは、レイアウトがソリューションに落ち着くまで、独自のtick()メソッドを(setIntervalまたはrequestAnimationFrameを介して)非同期的に繰り返し呼び出します。その時点で、そのalpha()値は0に等しいか、それに近づきます。

したがって、このソリューションプロセスを「早送り」したい場合、特定の要件に対して「十分に近い」を構成する値にレイアウトのアルファが達するまで、そのtick()メソッドを繰り返し呼び出します。 " 解決。そのようです:

_var force = d3.layout.force(),
    safety = 0;
while(force.alpha() > 0.05) { // You'll want to try out different, "small" values for this
    force.tick();
    if(safety++ > 500) {
      break;// Avoids infinite looping in case this solution was a bad idea
    }
}

if(safety < 500) {
  console.log('success??');
}
_

このコードの実行後、ノードの状態に基づいてレイアウトを描画できます。または、tickイベントにバインドしてレイアウトを描画している場合(つまり、force.on('tick', drawMyLayout))、バインドを実行する必要がありますafterこのコードが実行されます。 whileループの間に何百回も同期してレイアウトを不必要にレンダリングします。

JohnSは、このアプローチを1つの簡潔な機能に要約しました。このページのどこかで彼の答えを見てください。

30
meetamit

少し前にこのようなことを扱っていました。考慮すべきことがいくつかあります。

1)反復ティックは、平衡状態になるシステムをシミュレートしています。そのため、システムが落ち着く前に必要な回数だけtickを呼び出すことを避け、自動レイアウトを行う方法はありません。とはいえ、シミュレーションが機能するためにティックごとに視覚化を更新する必要はありません!実際、そうしないと、反復はずっと速くなります。私のコードの関連部分は次のとおりです。

var iters = 600; // You can get decent results from 300 if you are pressed for time
var thresh = 0.001;
if(!hasCachedLayout || optionsChanged || minorOptionsChanged) {
    force.start(); // Defaults to alpha = 0.1
    if(hasCachedLayout) {
        force.alpha(optionsChanged ? 0.1 : 0.01);
    }
    for (var i = iters; i > 0; --i) {
        force.tick();
        if(force.alpha() < thresh) {
            //console.log("Reached " + force.alpha() + " for " + data.nodes.length + " node chart after " + (iters - i) + " ticks.");
            break;
        }
    }
    force.stop();
}

これは同期的に実行され、実行後、すべてのノードとリンクのdom要素を作成します。小さいグラフの場合、これは非常に高速に実行されますが、大きなグラフ(100以上のノード)には遅延があります-単に計算コストがはるかに高くなります。

2)レイアウトをキャッシュ/シードできます。強制レイアウトは、初期化時にノードを均一に分散します位置が設定されていない場合!したがって、ノードでx属性とy属性が設定されていることを確認すると、これらが使用されます。特に、既存のグラフを更新するときは、以前のレイアウトのx位置とy位置を再利用します。

適切な初期レイアウトを使用すると、安定した構成に到達するためにlotより少ない反復が必要になります。 (これは、上記のコードでhasCachedLayoutが追跡するものです)。注意:同じレイアウトから同じノードを再利用する場合は、pxとpyをNaNに設定する必要があります。そうしないと、奇妙な結果が得られます。

11
Superboggly

他の回答に基づいて、私はこの方法を作りました:

function forwardAlpha(layout, alpha, max) {
  alpha = alpha || 0;
  max = max || 1000;
  var i = 0;
  while(layout.alpha() > alpha && i++ < max) layout.tick();
}
8
Ali Shakiba

たぶんforce.friction(0.5)、またはデフォルトの0.9よりも小さい他の数字が役立つでしょうか?少なくとも、ページの読み込み時に混chaとした印象が少なくなります。

4
Anton
        var width = 960,
          height = 500;

        var fill = d3.scale.category20();

        var force = d3.layout.force()
          .size([width, height])
          .nodes([{}]) // initialize with a single node
          .linkDistance(30)
          .charge(-60)
          .on("tick", tick);

        var svg = d3.select("body").append("svg")
          .attr("width", width)
          .attr("height", height)
          .on("mousedown", mousedown);

        svg.append("rect")
          .attr("width", width)
          .attr("height", height);

        var nodes = force.nodes(),
          links = force.links(),
          node = svg.selectAll(".node"),
          link = svg.selectAll(".link");

         // var cursor = svg.append("circle")
         //     .attr("r", 30)
         //     .attr("transform", "translate(-100,-100)")
         //     .attr("class", "cursor");

        restart();

        function mousedown() {
          var point = d3.mouse(this),
            node = {
              x: width / 2,
              y: height / 2,
              "number": Math.floor(Math.random() * 100)
            },
            n = nodes.Push(node);

          // add links to any nearby nodes
          /*  nodes.forEach(function(target) {
                    var x = target.x - node.x,
                        y = target.y - node.y;
                    if (Math.sqrt(x * x + y * y) < 30) {
                      links.Push({source: node, target: target});
                    }
                  });
                */
          restart();
        }

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

        function restart() {
          link = link.data(links);

          link.enter().insert("line", ".node")
            .attr("class", "link");

          node = node.data(nodes);

          // node.enter().insert("circle", ".cursor")
          //     .attr("class", "node")
          //     .attr("r", 5)
          //     .call(force.drag);

          var nodeEnter = node.enter().insert("svg:g", ".cursor")
            .attr("class", "node")
            .call(force.drag);

          nodeEnter.append("svg:circle")
            .attr("r", 5)

          nodeEnter.append("svg:text")
            .attr("class", "textClass")
            .attr("x", 14)
            .attr("y", ".31em")
            .text(function(d) {
              return d.number;
            });

          force.start();
        }
      rect {
          fill: none;
          pointer-events: all;
        }
        .node {
          fill: #000;
        }
        .cursor {
          fill: none;
          stroke: brown;
          pointer-events: none;
        }
        .link {
          stroke: #999;
        }
        .textClass {
          stroke: #323232;
          font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
          font-weight: normal;
          stroke-width: .5;
          font-size: 14px;
        }
        
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

あなたが探しているかもしれないものの例。レイアウトに挿入する前に、新しいノードのxおよびy属性を設定します。目的の場所は、svg要素の中心です。

0
timebandit