typeof Diary

VimとかJSとか。やったことのメモ。自分のため。

D3.jsのforce layoutで遊ぶ

前回はtransitionで、ひたすら円を動かし、そして消すをやりました。
今回は、見た目難しそうなアレをやってみます。

force layout

力学モデル
Wikipedia見ましょう。

あんなやつです。

作ってみる

データの用意

nodes用のデータ、links用のデータの2つを用意します。
データの例として、

var data = {
  nodes: [
    { name: 'Alice' },
    { name: 'Bob' },
    { name: 'Eve' }
  ],
  links: [
    { source: 0, target: 1 },
    { source: 0, target: 2 },
    { source: 1, target: 2 }
  ]
};

nodesにはAlice、Bob、Eveの3要素。
linksにはnodesのどの要素が、どの要素と繋がりがあるかを表しています。
例のデータだと、
source: 0, target: 1で、nodesの0番目(Alice)から1番目(Bob)へと繋がるようなイメージ。
Alice → Bob、Alice → Eve、 Bob → Eveと繋がるので、結果は三角形が出来上がります。

このようなデータだと良さ気です。

データを使って、作る

svg要素を作るところはカット。
force、line、circleを作ります。

var force, line, circle;

force = d3.layout.force()
  .nodes(data.nodes)
  .links(data.links)
  .size([200, 200])
  .linkDistance([100])
  .charge([-100])
  .start();

line = d3.selectAll('line')
  .data(data.links)
  .enter()
  .style({
    // お好みの線の色と太さ
  });

circle = d3.selectAll('circle')
  .data(data.nodes)
  .enter()
  .attr('r', 20)
  .call(force.drag);

forceのnodes、linksでデータを指定してやります。
linkDistanceはnodes同士の間隔。
chargeは反発力みたいな。そんな感じです。
正の値を入れると面白い動きをします。
そして、circleの最後のcall(force.drag)
これで、マウスドラッグが可能になります。

tick

最後にtickを。

force.on('tick', function () {
  line.attr({
    'x1': function (d) { return d.source.x; },
    'y1': function (d) { return d.source.y; },
    'x2': function (d) { return d.target.x; },
    'y2': function (d) { return d.target.y; }
  });

  circle.attr({
    'cx': function (d) { return d.x; },
    'cy': function (d) { return d.y; }
  });
});

これで大体完成となります。
意味なんかはreference読むのが一番かなぁと思っていますが、
きの基本形覚えてしまえば、いろいろ応用が利くはずです。

サンプル

今回もサンプルを作りました。
今回は、TwitterREST APIから自分のアカウントのフォローしているアカウント一覧を取得。
jsonファイルに落とし込んで、フォローしている人との繋がりを描画しました。
要するに、0が自分で、targetをフォローしているユーザとするだけです。

{ source: 0, target: 1 },
{ suurce: 0, target: 2 }
// ....

nodeはcircleではなく、imageを使用し、フォローしている方のアイコンを表示しています。

デモはこちら (URL間違ってたから修正w) ※重いので注意
ソースはこちら

最後に

難しそうなものですが、案外やってみると簡単に作れてしまいます。
どちらかというと、データに何を使うか、繋がりをどう表すのかが重要になりそうです。

今度は何をしよう。。。