49

私は d3 に手を出し始めたばかりで、学習曲線が非常に急勾配であることがわかります。プロセスは私が慣れ親しんでいるものとはまったく異なり、数学はほとんど私の頭の中にあります。

とにかく、私のプロジェクトは、システム間の統合のマップを表すフォース レイアウトで構成されています。この部分は非常にうまく機能しますが、大きな懸念事項が 1 つあります。これは、Michael Bostocks サイトのフォース ディレクテッド レイアウトのデモでも示されています。この後、いくつかの深刻な物理演算が引き継がれ、ノードが落ち着いてランダムな座標に落ち着くまで、かなり混乱した経路でノードを前後に送る重力をシミュレートします。これらの動きは、デモを初めて実行するときはクールですが、会社の管理者の観点からネットワーク インターフェイスの状態を表示しようとすると、サーバーが静止していないだけで、しばらくするとうんざりします。

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

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

4

6 に答える 6

40

上記のすべての回答は、オイシュタイン・アムンセンの質問を誤解しています。

開始時に力を安定させる唯一の方法は、node.x と node.ya に適切な値を設定することです。ノードは d3.js のデータであり、表現された DOM タイプではないことに注意してください。

たとえば、

nodes = [{"id": a}, {"id": b}, {"id": c}]

の中へ

d3.layout.force().nodes(nodes)

ノードの配列内のすべての要素のすべての .x および .y を設定する必要があります。これは次のようになります (coffeescript 内)

nodes = [{"id": a}, {"id": b}, {"id": c}]
for i in [0..2]
  nodes[i].x = 500 #the initial x position of node
  nodes[i].y = 300 #the initial y position of node
d3.layout.force().nodes(nodes).links(links)

ノードは force.start() 時の位置から開始します。これは混乱を避けるでしょう。

于 2013-10-04T08:14:29.967 に答える
30

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

したがって、このソリューション プロセスを「早送り」したい場合は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)にバインドを行う必要があります。そうしないと、ループ中に何百回も同期的にレイアウトを不必要にレンダリングすることになります。while

JohnS は、このアプローチを 1 つの簡潔な関数にまとめました。このページのどこかで彼の答えを参照してください。

于 2012-11-20T00:29:04.037 に答える
11

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

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 の位置を再利用します。

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

于 2012-11-21T14:40:11.070 に答える
8

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

function forwardAlpha(layout, alpha, max) {
  alpha = alpha || 0;
  max = max || 1000;
  var i = 0;
  while(layout.alpha() > alpha && i++ < max) layout.tick();
}
于 2013-05-03T02:29:48.247 に答える
4

、またはデフォルトの0.9force.friction(0.5)よりも小さい他の数値が役立つでしょうか? 少なくとも、ページの読み込み時に無秩序な印象を与えることはありません.

于 2014-03-18T09:22:00.907 に答える
0

	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 要素の中心です。

于 2015-06-29T01:22:09.947 に答える