LoginSignup
17
14

More than 5 years have passed since last update.

ぬるぬる動くグラフビジュアライズライブラリarbor.jsの使い方

Last updated at Posted at 2017-05-02

arbor.jsとは、下記のサンプルサイトのようなグラフが書けるライブラリです。
- sample
- halfviz

使ってみようと調べたところ、日本語の紹介サイトが少なかった1ので、まとめてみました。

本稿を読めば、
3.gif
こんなのや、
8.gif

こんなのが簡単に作れるようになります。

環境

  • Mac OSX 10.11.6
  • Safari 9.1.2
  • google Chrome 57.0.2987.133 (64-bit)  

初期構成

まずは、小さいグラフでnodeを■で表示するだけの物を作ってみます。
1.gif

ファイル配置

ライブラリは、jQueryとarbor.jsに加え、graphics.jsを使います。このファイルもarbor.js内においてあります。

まずは、libフォルダから、arbor.jsとarbor-tween.jsをコピーします。
libs.png

次に、demos/_フォルダから、jQueryのファイルと、graphics.jsをコピーします。
saport.png

最後に、index.htmlファイルとsample.jsファイルを作成します。ファイルの中身は、ソースコードの項に記載します。

最終的にこうなります。
files.png

ソースコード

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="js/jquery-1.6.1.min.js"></script>
<script src="js/jquery.address-1.4.min.js"></script>
<script src="js/arbor.js"></script>
<script src="js/arbor-tween.js"></script>
<script src="js/graphics.js"></script>
<script src="js/sample.js"></script>
<title>arbor.jsサンプル</title>
</head>
<body>
  <h1>arbor.js</h1>
<canvas id="viewport" width="280" height="280"></canvas>
</body>
</html>
sample.js
(function($){

  var Renderer = function(canvas){
    var canvas = $(canvas).get(0)
    var ctx = canvas.getContext("2d");
    var particleSystem

    var that = {
      init:function(system){
        particleSystem = system
        particleSystem.screenSize(canvas.width, canvas.height)
        particleSystem.screenPadding(80)
        that.initMouseHandling()
      },
      redraw:function(){
        ctx.fillStyle = "white"
        ctx.fillRect(0,0, canvas.width, canvas.height)

        particleSystem.eachEdge(function(edge, pt1, pt2){
          ctx.strokeStyle = "rgba(0,0,0, .333)"
          ctx.lineWidth = 1
          ctx.beginPath()
          ctx.moveTo(pt1.x, pt1.y)
          ctx.lineTo(pt2.x, pt2.y)
          ctx.stroke()
        })

        particleSystem.eachNode(function(node, pt){
          var w = 10
          ctx.fillStyle = "rgba(0,0,0, 1)"
          ctx.fillRect(pt.x-w/2, pt.y-w/2, w,w)
        })
      },


      initMouseHandling:function(){
        var dragged = null;
        var handler = {
          clicked:function(e){
            var pos = $(canvas).offset();
            _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
            dragged = particleSystem.nearest(_mouseP);

            if (dragged && dragged.node !== null){
              dragged.node.fixed = true
            }

            $(canvas).bind('mousemove', handler.dragged)
            $(window).bind('mouseup', handler.dropped)

            return false
          },
          dragged:function(e){
            var pos = $(canvas).offset();
            var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)

            if (dragged && dragged.node !== null){
              var p = particleSystem.fromScreen(s)
              dragged.node.p = p
            }

            return false
          },

          dropped:function(e){
            if (dragged===null || dragged.node===undefined) return
            if (dragged.node !== null) dragged.node.fixed = false
            dragged.node.tempMass = 1000
            dragged = null
            $(canvas).unbind('mousemove', handler.dragged)
            $(window).unbind('mouseup', handler.dropped)
            _mouseP = null
            return false
          }
        }
        $(canvas).mousedown(handler.clicked);

      },

    }
    return that
  }

  $(document).ready(function(){
    var sys = arbor.ParticleSystem(1000, 60, 0.5)
    sys.parameters({gravity:true})
    sys.renderer = Renderer("#viewport")
    sys.addNode('z')
    sys.addEdge('a','b')
  })

})(this.jQuery)

これでひとまず、動くものが出来ました。

htmファイル解説

jsのライブラリを読み込んでいます。canvas上に描画するため、canvasも置いています。idは、sample.jsで利用します。

htmlファイルは、最後までこれを使います。

jsファイル解説


particleSystem.eachEdge(function(edge, pt1, pt2){

この部分は、edge(nodeとnodeを結ぶ線)の描画処理です。この中でEdgeの見た目を決めています。今回は、半透明の黒で細い線を描画しています。


particleSystem.eachNode(function(node, pt)

この部分は、nodeの描画処理です。この中でnodeの見た目を決めています。今回は黒い小さな■を描画しています。


initMouseHandling:function

マウス操作の処理です。今回はnodeをつかんで移動させる処理が書いてあります。


$(document).ready(function()

グラフの定義が書いてあります。この部分のAPIはリファレンスに記載がありますので、
詳しくはそちらをご確認ください。
今回は、反発:1000、張力:600、摩擦:0.5で物理世界を構築しています。
aとbが線(Edge)で繋がっているnodeでzが単独のnodeです。
適当に数値やAPIを変えると動作がわかりやすいかもしれません。

見た目を変更する

node名を表示する

2.gif

nodeの描画処理を変更します。nodeにnode名を入れるように変更しました。

宣言部分に追加します。

var gfx = arbor.Graphics(canvas);

変える部分は、上記のnodeの描画処理の部分です。particleSystem.eachNodeに、addNodeで設定したnodeが入ってくるので、node.nameでnode名を取り出して、描画しています。

sample.js
var nodeBoxes = {}
        particleSystem.eachNode(function(node, pt){
          var label = node.name||""
          var w = ctx.measureText(""+label).width + 10
          if (!(""+label).match(/^[ \t]*$/)){
            pt.x = Math.floor(pt.x)
            pt.y = Math.floor(pt.y)
          }else{
            label = null
          }
          ctx.fillStyle = "rgba(0,0,0,1)"
          gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle})
          nodeBoxes[node.name] = [pt.x-w/2, pt.y-11, w, 22]

          // draw the text
          if (label){
            ctx.font = "12px Helvetica"
            ctx.textAlign = "center"
            ctx.fillStyle = "white"
            if (node.data.color=='none') ctx.fillStyle = '#333333'
            ctx.fillText(label||"", pt.x, pt.y+4)
            ctx.fillText(label||"", pt.x, pt.y+4)
          }
        })

nodeの色や形を変えてみる

3.gif

描画は、色を付けたり半透明を使ったり円を使ったり自由度が高いです。例として徳川家康の家系図を描画してみました。

sys.addNode('徳川家康',{who:"ieyasu"});
sys.addNode('養珠院',{who:"sokushitsu"});
sys.addNode('宝台院',{who:"sokushitsu"});

sys.addEdge('徳川家康','養珠院');
sys.addEdge('徳川家康','頼房');
sys.addEdge('徳川家康','頼宣');
sys.addEdge('徳川家康','宝台院');
sys.addEdge('徳川家康','秀忠');
sys.addEdge('養珠院','頼房');
sys.addEdge('養珠院','頼宣');
sys.addEdge('宝台院','秀忠');

まずは、家系図の定義を書きます。今回は、特殊なnodeには、whoプロパティをセットしました。これを利用して形や色を変える分岐を作ります。

if(node.data.who == "ieyasu")
          {
            ctx.fillStyle = "rgba(150,5,10,0.666)"
            gfx.oval(pt.x-w/2, pt.y-w/2, w,w, {fill:ctx.fillStyle})
          }
          else if(node.data.who == "sokushitsu")
          {
            ctx.fillStyle = "rgba(0,20,150,0.666)"
            gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle})
          }
          else
          {
            ctx.fillStyle = "rgba(0,0,0,0.666)"
            gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle})
          }
          nodeBoxes[node.name] = [pt.x-w/2, pt.y-11, w, 22]

このコードをgithub上に置きました。

nodeを画像に置き換える

9.gif

canvasの画像表示機能を使います。

sample.js
        particleSystem.eachNode(function(node, pt){
          var label = node.name||""
          var w = ctx.measureText(""+label).width + 20
          if (!(""+label).match(/^[ \t]*$/)){
            pt.x = Math.floor(pt.x)
            pt.y = Math.floor(pt.y)
          }else{
            label = null
          }

          var img = new Image();
          if(node.name == "AM")
          {
            img.src = "resource/AM.png";
            ctx.drawImage(img, pt.x-w/2, pt.y-w/2, w,w);
          }
          else if(node.name == "CH")
          {
            img.src = "resource/CH.png";
            ctx.drawImage(img, pt.x-w/2, pt.y-w/2, w,w);
          }
          else if(node.name == "CN")
          {
            img.src = "resource/CN.png";
            ctx.drawImage(img, pt.x-w/2, pt.y-w/2, w,w);
          }
          else if(node.name == "EN")
          {
            img.src = "resource/EN.png";
            ctx.drawImage(img, pt.x-w/2, pt.y-w/2, w,w);
          }
          else if(node.name == "JP")
          {
            img.src = "resource/JP.png";
            ctx.drawImage(img, pt.x-w/2, pt.y-w/2, w,w);
          }
          else
          {
            ctx.fillStyle = "rgba(0,0,0,0.666)"
            gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle})
          }
          nodeBoxes[node.name] = [pt.x-w/2, pt.y-11, w, 22]
        })

nodeの設定をします。

sample.js
    var sys = arbor.ParticleSystem(1000, 60, 0.5)
    sys.parameters({gravity:true})
    sys.renderer = Renderer("#viewport")
    sys.addNode('AM');
    sys.addNode('CH');
    sys.addNode('JP');
    sys.addNode('CN');
    sys.addNode('EN');
    sys.addEdge('AM','CN');
    sys.addEdge('CN','JP');
    sys.addEdge('JP','CH');
    sys.addEdge('CH','EN');
    sys.addEdge('EN','AM');

srcは、githubのリポジトリのimageブランチをご確認ください。

使用例

使いどころが難しそうなライブラリですが、分散ネットワーク(マストドンなど)を俯瞰して眺める用や、ニューラルネットの表現などにも使える可能性があるように思います。

他にも数学的な表現で使える場面があるのではないかと思います。

試しに、ある数字に割り算可能な数字が何であるかをグラフにしてみました。

8.gif

srcは、githubのリポジトリのgh-pagesブランチをご確認ください。

上記の動くサンプルサイトを置きました。
https://takahiroyamamoto.github.io/arbor-sample/

アルゴリズム

function factoring(seed,parent)
  {
    for(var i = Math.floor(seed/2) ; 1 < i ; i--)
    {
      var alreadyHave = false;
      if(seed % i == 0)
      {
        fact.push({'parent':parent,'seed':seed,'value':i});
        factoring(i,seed);
      }
    }
  }

seedの数で割り切れるiを探索し、見つけたらそれを保存して再帰探索するものです。


  1. 実際に見つかったのは、このサイトのみなのですが、大変参考にさせて頂きました。ありがとうございました。 

17
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14