21
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

力学モデル描画ライブラリー Springy のご紹介

Last updated at Posted at 2015-06-28

公式サイト:springy

使うと何が嬉しいのか

(グラフ理論的な意味での)グラフを自動的にそれなりに綺麗な配置で表示したいときに使います。

デモ

どうやって実現しているのか

力学モデル(Force-directed graph drawing)アルゴリズムを使ってノードの位置を決定します。

使い方の手引き

ファイル構成

主要なオブジェクト

主要な三つのオブジェクト

  • Springy.Graph : グラフデータを持つモデルオブジェクト
  • Layout.ForceDirected : 力学レイアウトアルゴリズム
  • Springy.Renderer : レイアウトの制御用インターフェース

API

API設計には多少の混乱が見られます。

Springy.GraphのCRUD

Node CREATE

Nodeコンストラクタ

Springy.Node オブジェクトを作ります。

  • id : 識別子。nodeSetで取得するときに使います
  • data : 任意のデータ。オブジェクトを入れるのが一般的
new Springy.Node(1234, {label: 'ABC'});
addNode

Springy.Node オブジェクトをグラフに追加します。
idを指定して追加したい場合に使います。

  • node : Springy.Node オブジェクト
graph.addNode(new Springy.Node(1234))
addNodes

複数のノードを一括して使いたいときに使います。
引数は(複数の)文字列です。
引数の数は任意です。

graph.addNodes('mark', 'higgs', 'other', 'etc');

指定した文字列はidとしても使われるので、重複は許されません。
addEdgesと組み合わせて、id指定でエッジを追加する想定です。

:thumbsdown: Springy.GraphのAPIでなく、Helper関数として提供されていれば・・・

newNode

idを自動採番してノードを追加します。

graph.newNode({label: '1'});

:thumbsdown: newNodeという名前はちょっと・・・

Node READ

nodeSet

ノードはオブジェクトで管理されています。
idを指定して取得できます。

var node = graph.nodeSet(1234)

Node UPDATE

ノードオブジェクトを直接編集します。
特にAPIは提供されていません。

Node DELETE

removeNode

ノードを削除します。付随するエッジも自動的に削除します。

graph.removeNode(graph.nodeSet(1234))

:thumbsdown: idが指定できたら嬉しいですね。

filterNodes

関数にマッチしたノードを残して、それ以外を削除します。

graph.filterNodes(function(node){return node.id !== 1234;})

:thumbsdown: 破壊的な関数にfilterHogeという名前をつけるのはちょっと・・・

Edge CREATE

Edgeコンストラクタ

Springy.Edge オブジェクトを作ります。

  • id : 識別子。
  • sourge : ソースノード
  • target : ターゲットノード
  • data : 任意のデータ。オブジェクトを入れるのが一般的
var node1 = graph.newNode({label: '1'});
var node2 = graph.newNode({label: '2'});

new Springy.Edge(123, node1, node2, {labes: '1-2'});

:thumbsdown: エッジに任意のidをつけたいことがあまりないので使いません。

addEdge

Springy.Edge オブジェクトをグラフに追加します。

  • edge : Springy.Edge オブジェクト
graph.addNode(new Springy.Node(1234))

:thumbsdown: エッジに任意のidをつけたいことがあまりないので使いません。

addEdges

複数のエッジを一括でグラフに追加します。

  • 無限引数 : [ノードid1, ノードid2, data]の配列
graph.addEdges([1, 2], [3, 4])

:thumbsup: ノードidで指定したい場合に便利です。

newEdge

idを自動採番してエッジを追加します。

  • sourge : ソースノード
  • target : ターゲットノード
  • data : 任意のデータ。オブジェクトを入れるのが一般的
var node1 = graph.newNode({label: '1'});
var node2 = graph.newNode({label: '2'});

graph.newEdge(node1, node2, {labes: '1-2'});

ノードとエッジを同時に追加するときに便利です。

Edge READ

getEdges

ノードを指定してエッジを取得します。

  • node1 : ノード1
  • node2 : ノード2
var edges = graph.getEdges(node1, node2);

:thumbsdown: idが指定できたら嬉しいです。

Edge UPDATE

エッジオブジェクトを直接編集します。
特にAPIは提供されていません。

Edge DELETE

removeEdge

エッジを削除します。

graph.removeEdge(graph.getEdges(node1, node2))

:thumbsdown: idが指定できたら嬉しいです。

filterNodes

関数にマッチしたエッジを残して、それ以外を削除します。

graph.filterNodes(function(node){return edge.source.id !== 1 || edge.target.id !== 2;})

:thumbsdown: 破壊的な関数にfilterHogeという名前をつけるのはちょっと・・・

Springy.Graphのイベント

addGraphListener

  • obj : graphChangedという関数を持つオブジェクトを指定します。引数は何もくれません。
graph.addGraphListener({
  graphChanged: () => console.log(graph.edges, graph.nodes)
})

:thumbsdown: 特定の名前の関数を持ったオブジェクトを指定するのはあまり見ないAPIです。
:thumbsdown: 引数で変更内容かgraphのスナップショットを通知してくれると嬉しいです。

Springy.Layout.ForceDirectedのコンストラクタ

  • graph : グラフオブジェクト
  • stiffness : ばね定数
  • repulsion : 斥力
  • damping : 減衰係数
  • minEnergyThreshold : レンダリングを止める閾値。初期値0.01
var graph = new Springy.Graph(),
  layout = new Springy.Layout.ForceDirected(graph, 400.0, 400.0, 0.5)

:thumbsdown: minEnergyThresholdにだけ初期値があるのはなぜ?

Springy.Rendererのコンストラクタ

  • layout : レイアウトオブジェクト
  • clear : クリア時のコールバック関数
  • drawEdge : エッジ描画用コールバック関数。引数はedge, p1, p2。
  • drawNode : ノード描画用コールバック関数。引数はnode, p。
  • onRenderStop : Renderストップ時のコールバック。使い道は不明
  • onRenderStart : Renderスタート時のコールバック。使い道は不明

:thumbsdown: イベントにしてくれればコンストラクタ以外でも指定できて便利です。

レイアウトと描画領域のMapping方法

レイアウト中ではノードはサイズ制限の無い、無限の空間を移動しています。
drawEdgedrawNodeコールバックで渡される位置は、レイアウト中の座標です。

描画する際は有限の領域に描画する必要があります。
そこで以下のようなアルゴリズムで座標を返還します。

var currentBB = layout.getBoundingBox(),
  size = currentBB.topright.subtract(currentBB.bottomleft),
  x = p.subtract(currentBB.bottomleft).divide(size.x).x,
  y = p.subtract(currentBB.bottomleft).divide(size.y).y

layout.getBoundingBoxでノードが存在する領域の座標を取得できます。

また、描画領域側でノード移動した場合は逆変換が必要です。

var currentBB = layout.getBoundingBox(),
  size = currentBB.topright.subtract(currentBB.bottomleft),
  px = x * size.x + currentBB.bottomleft.x,
  py = y * size.y + currentBB.bottomleft.y

:thumbsdown: 最初から百分率でノーマライズした座標で入出力できればと思います。

レイアウト中のノードの移動方法

ノードの位置はグラフではなくレイアウトが保持しています。
レイアウトからノードの位置を取得して、更新します。

var nodePoint = layout.point(graph.nodeSet[id])

nodePoint.p.x = px
nodePoint.p.y = py

:thumbsdown: idとx, yを指定して更新できるAPIがあると嬉しいです。

21
24
0

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
21
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?