D3.js サンプル orgoShmorgo を読んでソーシャルグラフを考えるの巻き – その弐

Pocket

初回に続いてサンプルを読み込みます。

その前に、サンプルから見える機能仕様をリストアップします。
これらがどういった記述で書かれているのかがわかれば、サンプルの解析は終了となります。

まずは仕様として、svgに含まれる要素をデベロッパーツールを利用して抽出してみます。

FFdevview

以下の3タイプのSVG用要素を確認できました。

  • defs
    • feColorMatrix
    • feGaussianBlur
  • g
    • line
  • g
    • circle
    • text

意外とシンプルですね。いや、こんなものか。

基本的に要素は線と円で構成されているようです。

せっかくなので、属性についても整理してみます。

連続する数字を「n」に置き換えて、代表的なグループを抽出すると以下のような形式でした。


<g class="link">
	<line y2="n.n" x2="n.n" y1="n.n" x1="n.n" style="stroke-width: 2px;"></line>
</g>
<g class="link">
	<line y2="n.n" x2="n.n" y1="n.n" x1="n.n" style="stroke-width: 8px;"></line>
	<line y2="n.n" x2="n.n" y1="n.n" x1="n.n" class="double" style="stroke-width: 4px;"></line>
</g>
<g transform="translate(n.n,n.n)" class="node">
	<circle style="fill: rgb(n, n, n);" r="n.n"></circle>
	<text text-anchor="middle" dy=".nem">C</text>
</g>

まあまあ、予測の範囲内です。

では、機能について考えてみます。

まずイベント系

  • クリック 円 発光する
  • ドラッグ&ドロップ 円 うにうに動く

描画系

  • ノードが分散する角度が何かの規則で算出されている?

発光するのは、前回glow関数が使われているらしいことはわかっているので、前回のつづきから、「buildMolecule」を読みます。

link = link.data(links, function (d) {return d.id; }); ってなんだ

link.data()というのは、既に宣言済みのlinks


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

をlinkに代入している訳ですが、さて、このlinksとlinkとはなんぞということになります。

そこでまず、

var links = force.links()

これはなんだと。
よく見ると、forceも、直前で宣言されています。


  var force = d3.layout.force()
        .nodes(nodesList)
        .links(linksList)
        .size([width, height])
        .charge(-400)
        .linkStrength(function (d) { return d.bondType * 1;})
        .linkDistance(function(d) { return radius(d.source.size) + radius(d.target.size) + 20; })
        .on("tick", tick);

うーん、なんか出て来たぞ。
d3.layout というのがなんかありそうですね。
どうやら円(node)の配置を決めていそうな感じがします。

さすがに、これだけを見ていても何も浮かばないので、検索してみます。

・・・・・・・・ふむふむ。

ネットの記事によると、force layoutは力学モデルのことらしいです。このforceは「物理」ですね。

リファレンスは https://github.com/mbostock/d3/wiki/Force-Layout ですね。

ちなみに、layoutにはforceを合わせて、12の種類があるみたいです。
Hierarchyなどは、更に細分化したレイアウトが用意されているようです。
このあたりが、D3の魅力のようですね。

Many layouts are built in to D3 itself:

  • Bundle – apply Holten’s hierarchical bundling algorithm to edges.
  • Chord – produce a chord diagram from a matrix of relationships.
  • Cluster – cluster entities into a dendrogram.
  • Force – position linked nodes using physical simulation.
  • Hierarchy – derive a custom hierarchical layout implementation.
  • Histogram – compute the distribution of data using quantized bins.
  • Pack – produce a hierarchical layout using recursive circle-packing.
  • Partition – recursively partition a node tree into a sunburst or icicle.
  • Pie – compute the start and end angles for arcs in a pie or donut chart.
  • Stack – compute the baseline for each series in a stacked bar or area chart.
  • Tree – position a tree of nodes tidily.
  • Treemap – use recursive spatial subdivision to display a tree of nodes.

さて、話題を「Force Layout」に戻します。

以下のメソッドが用意されているようです。

  • # force.size([width, height])
  • # force.linkDistance([distance])
  • # force.linkStrength([strength])
  • # force.friction([friction])
  • # force.charge([charge])
  • # force.chargeDistance([distance])
  • # force.theta([theta])
  • # force.gravity([gravity])
  • # force.nodes([nodes])
  • # force.links([links])
  • # force.start()
  • # force.alpha([value])
  • # force.resume()
  • # force.stop()
  • # force.tick()
  • # force.on(type, listener)
  • # force.drag()

いや、マジすげーな。
多!!多謝!!

なんで、D3.jsのことを今まで知らなかったのかな。。

さて、このうち、今回のサンプルで利用されているのは以下のとおりです。

nodes([nodes]), links([links])

以下のようなノードと、その関係を代入するためのメソッドです。
力学モデルの基本要素ですね。


{
  "nodes":[
    {"name":"Myriel","group":1},
    {"name":"Napoleon","group":1}
  ],
  "links":[
    {"source":1,"target":0,"value":1},
    {"source":2,"target":0,"value":8}
  ]
}

size([width, height])

みたまんま、サイズの指定です。ただ、サイズはgaravityに関係するらしいです。
リファレンスを英語で読んで、なんとなーくわかったのですが、よりわかりやすくするために、Google先生の翻訳にぶっこんでみました。

 サイズが指定されている場合、 x、yを表す数字の指定された2つの要素のアレイで使用可能なレイアウトサイズを設定します。サイズが指定されていない場合は、現在のサイズ、デフォルトを返します[ 1,1 ] 。重心、初期ランダムな位置:サイズは2力指向のレイアウトの側面に影響を与えます。重心が単に[X / 2 、 Y / 2 ] 。彼らはXを持っていない場合、ノードは、力のレイアウトに追加され、 Yが既に設定属性場合、これらの属性は、それぞれの範囲内の均一なランダム分布[ 0 、 X ]と[ 0 、 Y ]を使用して初期化されます。

うーん。なるほど。これは正に Force Layoutですね。サイズによって、配置というか、オブジェクトの分布の仕方が計算されるようです。

charge([charge])

英語を読んでもピンと来なかったので、いきなりGoogle先生の翻訳にぶっこみました。

チャージが指定されている場合、指定された値に充電強度を設定します。電荷が指定されていない場合は、充電電流の強さ、-30にデフォルト値を返します。電荷が一定である場合、すべてのノードが同一の電荷を有します。電荷が関数であればそうでない場合、その関数は、力のレイアウトとして、この文脈で、ノードとそのインデックスを渡され、(順番に)各ノードに対して評価されます。関数の戻り値は、各ノードの電荷を設定するために使用されます。レイアウトが起動するたびに機能が評価されます。

ノード反発に負の値を設定すると、ノードの名所で正の値をもたらす一方。グラフレイアウトのために、負の値を使用する必要があります。 N体シミュレーションのために、正の値を使用することができます。すべてのノードが同じ電荷および質量の微小ポイントであると仮定されます。電荷力は、各ティックのための四分木を計算し、バーンズ·ハットアルゴリズムによって効率的に実装されています。ゼロに充電力を設定すると、n体の力を必要としない場合は著しくパフォーマンスを向上させることができ四分木の計算を無効にします。

なるほどねー。これは面白い。チャージといのは、wikipedia:チャージ(物理学)のことのようです。荷電による反発や引き寄せてきな考えということですかね。
サンプルでいうと、マイナスの値なので、反発している状態なんでしょうかね。

linkStrength([strength])

強度が指定されている場合は、 [0,1]の範囲内の指定された値へのリンクの強度(剛性)を設定します。強度が指定されていない場合、 1のデフォルト強度が一定である場合には、すべてのリンクが同じ強度を有するレイアウトの現在のリンクの強さを返します。強度が関数であればそれ以外の場合、この関数は、 (順番に)リンクごとに評価される力のレイアウトとして、このコンテキストとのリンクとそのインデックスを渡されます。関数の戻り値は、それぞれのリンクの強さを設定するために使用されています。レイアウトが起動するたびに機能が評価されます。

予備思考なしに、翻訳してました。

まぁ張力ということですかね。サンプルノードをドラッグすると、結びつきが強いものと弱いものがあるので、そのあたりの設定のように思われます。(間違えてたらすみませんね。。)

linkDistance([distance])

単なる距離の設定でしょ?という割には説明文が長いので翻訳。

距離が指定されている場合、指定された値にリンクされたノード間の目標距離を設定します。距離が指定されていない場合、距離が一定である場合には、すべてのリンクが同じ距離である20デフォルトレイアウトの現在のリンク距離を返します。距離が関数であればそうでない場合、この関数は、(順番に)リンクごとに評価される力のレイアウトとして、このコンテキストとのリンクとそのインデックスを渡されます。関数の戻り値は、各リンクの距離を設定するために使用されています。レイアウトが起動するたびに機能が評価されます。

他の力指向のレイアウトが、弱い幾何学的制約に一般的であるようなリンクは、「ばね力」として実装されていません。レイアウトの各目盛りは、リンクされたノードの各対の間の距離を計算し、ターゲット距離と比較されます。所望の距離に収束するように、リンクは、その後、互いに向かって、または互いから離れて移動されます。位置ベルレ積分の上に制約緩和のこの方法は非常にばね力を用いて、従来の方法よりも安定であり、また、このような階層階層化などのダニイベントリスナーで他の制約の柔軟な実装が可能になります。

まぁ、いろいろ書いてありますが、所詮距離と。つぎいってみよー!!(いかりや

on(type, listener)

・・・・やべぇ、英文が頭に入って来ない。。とりあえず、全文翻訳いっときます。
とりあえず、typeには start/tick/endがあるらしいけど。orgoShmorgoのサンプルは .on(“tick”,tick) ってしていますね。

力レイアウトから指定された型のイベントを受け取るために、指定されたリスナーを登録します。現在は、 「スタート」 「ダニ」 、および「終了」のイベントがサポートされています。

リスナー関数に渡されるイベントオブジェクトはd3.dispatch ( )プロセスを使用して作成したカスタムオブジェクトです。タイプ(文字列、 「終了」 「ダニ」 、または、 「スタート」のいずれか) 、およびアルファ冷却パラメータの現在の値であるα 、 ( 0の間の数と1 ) :イベントオブジェクトには、 2つのプロパティがあります。 event.alphaプロパティは、レイアウトの進行状況を監視したり、独自のカスタム調整を制御するために使用することができます。

「開始」イベントは、シミュレーションの初期起動の両方に送出され、いつでもシミュレーションが再起動されます。

「チック」のイベントはシミュレーションの各目盛りのために派遣されています。ノードとリンクの表示位置を更新するためのイベントをチェックするために耳を傾けます。たとえば、最初にそのようなノードとリンクを表示している場合:

var link = vis.selectAll("line")
    .data(links)
  .enter().append("line");

var node = vis.selectAll("circle")
    .data(nodes)
  .enter().append("circle")
    .attr("r", 5);

You can set their positions on tick:

force.on("tick", function() {
  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("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
});

我々はすべてのティック上のノードを再選択する必要がないように、このケースでは、初期化での選択ノードとリンクを保存されてきました。ご希望の場合は、異なるノードとリンクを表示することができます。たとえば、記号ではなく、円を使用する場合があります。

パラメータを冷却シミュレーション内部アルファはカットオフ値( 0.005 )とがゼロに設定されている以下に低下すると「終了」イベントが送出されます。

うーん。。ちょっと謎です。

引数に入れているtickはこれなんでしょうけど。初期値を入れているイメージなのかなぁ。


	  function tick() {
	  	//Update old and new elements
	    link.selectAll("line")
	        .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 + ")"; });
	  }

ま、ちょっとまだまだ勉強が必要のようですが、キモになる力学モデルを模した(沿った?)レイアウトはForce Layout d3.layout.force() だということ、利用されているメソッドがなんとなくわかったところで、読み込みは次回に続きます。

 

D3.js サンプル orgoShmorgo を読んでソーシャルグラフを考えるの巻き – その参 へ続く

Pocket