レイアウトプラグインの作成
xxx
という名前の新しいレイアウトプラグインを作成するには、最初に2つの関数xxx_layout
とxxx_cleanup
を提供する必要があります。これらのセマンティクスは以下で説明します。
レイアウト
void xxx_layout(Agraph_t * g)
グラフを初期化します。
-
アルゴリズムが共通のエッジルーティングコードを使用する場合は、
setEdgeType (g, ...);
を呼び出す必要があります。 -
各ノードについて、
common_init_node
とgv_nodesize
を呼び出します。 -
アルゴリズムが
spline_edges()
を使用してエッジをルーティングする場合は、ノードの座標をND_pos
に格納する必要があるため、ここで割り当てる必要があります。これと上記の2つの呼び出しは、neato_init_node()
の呼び出しによってすべて処理されます。 -
各エッジについて、
common_init_edge
を呼び出します。 -
アルゴリズムは、必要な他のデータ構造を割り当てる必要があります。これには、
A*info_t
フィールドのフィールドが含まれる場合があります。さらに、これらの各フィールドには、アルゴリズムが追加データを格納するために使用できるvoid* alg;
サブフィールドが含まれています。cgraphに移行すると、これらはすべてアルゴリズム固有のレコードに置き換えられます。 -
グラフをレイアウトします。完了すると、各ノードは
ND_coord_i(n)
の点に座標が格納され、各エッジはED_spl(e)
で記述されたレイアウトを持つ必要があります。(注意:バージョン2.21以降、ND_coord_i
はND_coord
に置き換えられ、これらは浮動小数点座標になりました。)
エッジを追加するには、3つの関数が利用可能です
spline_edges1 (Agraph_t*, int edgeType)
ノードの座標がND_coord_i
に格納され、GD_bb
が設定されていると仮定します。この関数は、各エッジについて、適切なデータを構築し、ED_spl
に格納します。spline_edges0 (Agraph_t*)
ノードの座標がND_pos
に格納され、GD_bb
が設定されていると仮定します。この関数は、設定されている場合はratio属性を使用し、ND_pos
の値をND_coord_i
にコピーし(インチからポイントに変換)、setEdgeType()
で指定されたエッジタイプを使用してspline_edges1を呼び出します。spline_edges (Agraph_t*)
ノードの座標がND_pos
に格納されていると仮定します。この関数は、gの境界ボックスを計算し、GD_bb
に格納し、次にspline_edges0()
を呼び出します。
アルゴリズムが連結成分のみで動作する場合は、packライブラリを使用してコンポーネントを取得し、個別にレイアウトし、ユーザーの指定に基づいてそれらを一緒にパックできます。一般的なスキーマを以下に示します。詳細な例については、twopi
、circo
、neato
、またはfdp
のコードを参照してください。
Agraph_t **ccs;
Agraph_t *sg;
Agnode_t *c = NULL;
int ncc;
int i;
ccs = ccomps(g, &ncc, 0);
if (ncc == 1) {
/* layout nodes of g */
adjustNodes(g); /* if you need to remove overlaps */
spline_edges(g); /* generic edge routing code */
} else {
pack_info pinfo;
pack_mode pmode = getPackMode(g, l_node);
for (i = 0; i < ncc; i++) {
sg = ccs[i];
/* layout sg */
adjustNodes(sg); /* if you need to remove overlaps */
}
spline_edges(g); /* generic edge routing */
/* initialize packing info, e.g. */
pinfo.margin = getPack(g, CL_OFFSET, CL_OFFSET);
pinfo.doSplines = 1;
pinfo.mode = pmode;
pinfo.fixed = 0;
packSubgraphs(ncc, ccs, g, &pinfo);
}
for (i = 0; i < ncc; i++) {
agdelete(g, ccs[i]);
}
free(ccs);
ルートグラフでのみ設定された属性に依存する場合は、サブグラフのレイアウトに注意してください。連結成分では、コンポーネントのパック前(上記のように)またはパック後(circoを参照)に各コンポーネントでエッジを追加できます。
グラフに0または1個のノードがある場合、またはエッジがない場合は、些細なケースをチェックすることをお勧めします。
xxx_layout
の最後に、次を呼び出します
dotneato_postprocess(g);
次のテンプレートは、切断されたグラフの処理とノードのオーバーラップの削除の問題を無視して、ほとんどの場合に機能します
static void
xxx_init_node(node_t * n)
{
neato_init_node(n);
/* add algorithm-specific data, if desired */
}
static void
xxx_init_edge(edge_t * e)
{
common_init_edge(e);
/* add algorithm-specific data, if desired */
}
static void
xxx_init_node_edge(graph_t * g)
{
node_t *n;
edge_t *e;
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
xxx_init_node(n);
}
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
for (e = agfstout(g, n); e; e = agnxtout(g, e)){
xxx_init_edge(e);
}
}
}
void
xxx_layout (Agraph_t* g)
{
xxx_init_node_edge(g);
/* Set ND_pos(n) for each node n */
spline_edges(g);
dotneato_postprocess(g);
}
クリーンアップ
void xxx_cleanup(Agraph_t * g)
レイアウトで割り当てられたリソースを解放します。
各ノードおよびエッジに対してgv_cleanup_node
およびgv_cleanup_edge
の呼び出しで終了します。これにより、スプラインラベル、ND_pos
、形状がクリーンアップされ、A*info_t
が0になるため、これらは最後に発生する必要がありますが、必要に応じて明示的なxxx_cleanup_node
およびxxx_cleanup_edge
の一部にすることができます。
最後に、次を行う必要があります
if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
これは、レイアウトコードがこの構造がクリーンであると想定しているため、グラフを再度レイアウトするために必要です。
libgvc
は、ルートグラフに対して最終的なクリーンアップを行い、描画を解放し、ラベルを解放し、ルートグラフの Agraphinfo_t
をゼロにします。
次のテンプレートは、ほとんどの場合に機能します。
static void xxx_cleanup_graph(Agraph_t * g)
{
/* Free any algorithm-specific data attached to the graph */
if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
}
static void xxx_cleanup_edge (Agedge_t* e)
{
/* Free any algorithm-specific data attached to the edge */
gv_cleanup_edge(e);
}
static void xxx_cleanup_node (Agnode_t* n)
{
/* Free any algorithm-specific data attached to the node */
gv_cleanup_node(e);
}
void xxx_cleanup(Agraph_t * g)
{
Agnode_t *n;
Agedge_t *e;
for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
xxx_cleanup_edge(e);
}
xxx_cleanup_node(n);
}
xxx_cleanup_graph(g);
}
ほとんどのレイアウトは neato
と同様の補助ルーチンを使用するため、エントリポイントは plugin/neato_layout
に追加できます。
gvlayout_neato_layout.c
に追加します。
gvlayout_engine_t xxxgen_engine = {
xxx_layout,
xxx_cleanup,
};
そして、次の行を
{LAYOUT_XXX, "xxx", 0, &xxxgen_engine, &neatogen_features},
gvlayout_neato_types
に追加し、新しい enum LAYOUT_XXX
をそのファイルの layout_type
に追加します。
上記により、新しいレイアウトは neato
プラグインの上に乗っかることができますが、プラグインの再構築が必要です。一般的に、ユーザーはレイアウトプラグインを完全に別々に構築できます(そしておそらくそうすべきです)。
これを行うには、xxx_layout
と xxx_cleanup
を記述した後、次のことが必要です。
-
型とデータ構造を追加します。
typedef enum { LAYOUT_XXX } layout_type; static gvlayout_features_t xxxgen_features = { 0 }; gvlayout_engine_t xxxgen_engine = { xxx_layout, xxx_cleanup, }; static gvplugin_installed_t gvlayout_xxx_types[] = { {LAYOUT_XXX, "xxx", 0, &xxxgen_engine, &xxxgen_features}, {0, NULL, 0, NULL, NULL} }; static gvplugin_api_t apis[] = { {API_layout, &gvlayout_xxx_types}, {(api_t)0, 0}, }; gvplugin_library_t gvplugin_xxx_layout_LTX_library = { "xxx_layout", apis };
-
これらすべてを、名前が文字列
gvplugin_
を含む動的ライブラリにまとめ、他の Graphviz プラグインと同じディレクトリにライブラリをインストールします。たとえば、Linux システムでは、dot レイアウトプラグインはライブラリlibgvplugin_dot_layout.so
にあります。 -
dot -c
を実行して、構成ファイルを再生成します。
注記
- 追加のレイアウトは、
gvlayout_xxx_types
に追加の行として追加できます。 - 明らかに、ほとんどの名前と文字列は任意にすることができます。1つの制約は、
gvplugin_library_t
型の外部識別子が_LTX_library
で終わる必要があることです。さらに、gvlayout_xxx_types
の各エントリの文字列xxx
は、レイアウトアルゴリズムを識別するために使用される名前であるため、他のレイアウト名と区別する必要があります。 - レイアウトアルゴリズムの機能は現在、ビットのフラグに制限されており、サポートされているフラグは
LAYOUT_USES_RANKDIR
のみです。これにより、レイアウトはrankdir
属性を有効にできます。
レイアウトアルゴリズムについて静的に知っているアプリケーションには変更が必要です。
Automake設定
コードを Graphviz ソフトウェアに統合し、そのビルドシステムを使用する場合は、以下の手順に従ってください。独自のビルドソフトウェアを使用してプラグインをビルドおよびインストールすることもできます。
- ソフトウェアを
lib/xxxgen
に配置し、上記で説明したフックをgvlayout_neato_layout.c
に追加します。 lib/xxxgen
に、Makefile.am
(lib/fdpgen/Makefile.am
のような簡単な例に基づいたもの)を提供します。lib/Makefile.am
で、xxxgen
をSUBDIRS
に追加します。configure.ac
で、lib/xxxgen/Makefile
をAC_CONFIG_FILES
に追加します。lib/plugin/neato_layout/Makefile.am
で、$(top_builddir)/lib/xxxgen/libxxxgen_C.la
をlibgvplugin_neato_layout_C_la_LIBADD
に挿入します。configure
だけでは間違った推測をする可能性があるため、autogen.sh
を実行することを忘れないでください。
これは、システムにさまざまな automake ツールの適切なバージョンがあることも前提としています。