レイアウトプラグインの作成

カスタムレイアウトエンジンの作成方法。

xxxという名前の新しいレイアウトプラグインを作成するには、最初に2つの関数xxx_layoutxxx_cleanupを提供する必要があります。これらのセマンティクスは以下で説明します。

レイアウト

void xxx_layout(Agraph_t * g)

グラフを初期化します。

  • アルゴリズムが共通のエッジルーティングコードを使用する場合は、setEdgeType (g, ...);を呼び出す必要があります。

  • 各ノードについて、common_init_nodegv_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_iND_coordに置き換えられ、これらは浮動小数点座標になりました。)

エッジを追加するには、3つの関数が利用可能です

  1. spline_edges1 (Agraph_t*, int edgeType)ノードの座標がND_coord_iに格納され、GD_bbが設定されていると仮定します。この関数は、各エッジについて、適切なデータを構築し、ED_splに格納します。
  2. spline_edges0 (Agraph_t*)ノードの座標がND_posに格納され、GD_bbが設定されていると仮定します。この関数は、設定されている場合はratio属性を使用し、ND_posの値をND_coord_iにコピーし(インチからポイントに変換)、setEdgeType()で指定されたエッジタイプを使用してspline_edges1を呼び出します。
  3. spline_edges (Agraph_t*)ノードの座標がND_posに格納されていると仮定します。この関数は、gの境界ボックスを計算し、GD_bbに格納し、次にspline_edges0()を呼び出します。

アルゴリズムが連結成分のみで動作する場合は、packライブラリを使用してコンポーネントを取得し、個別にレイアウトし、ユーザーの指定に基づいてそれらを一緒にパックできます。一般的なスキーマを以下に示します。詳細な例については、twopicirconeato、または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_layoutxxx_cleanup を記述した後、次のことが必要です。

  1. 型とデータ構造を追加します。

    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 };
    
  2. これらすべてを、名前が文字列 gvplugin_ を含む動的ライブラリにまとめ、他の Graphviz プラグインと同じディレクトリにライブラリをインストールします。たとえば、Linux システムでは、dot レイアウトプラグインはライブラリ libgvplugin_dot_layout.so にあります。

  3. dot -c を実行して、構成ファイルを再生成します。

注記

  • 追加のレイアウトは、gvlayout_xxx_types に追加の行として追加できます。
  • 明らかに、ほとんどの名前と文字列は任意にすることができます。1つの制約は、gvplugin_library_t 型の外部識別子が _LTX_library で終わる必要があることです。さらに、gvlayout_xxx_types の各エントリの文字列 xxx は、レイアウトアルゴリズムを識別するために使用される名前であるため、他のレイアウト名と区別する必要があります。
  • レイアウトアルゴリズムの機能は現在、ビットのフラグに制限されており、サポートされているフラグは LAYOUT_USES_RANKDIR のみです。これにより、レイアウトは rankdir 属性を有効にできます。

レイアウトアルゴリズムについて静的に知っているアプリケーションには変更が必要です。

Automake設定

コードを Graphviz ソフトウェアに統合し、そのビルドシステムを使用する場合は、以下の手順に従ってください。独自のビルドソフトウェアを使用してプラグインをビルドおよびインストールすることもできます。

  1. ソフトウェアを lib/xxxgen に配置し、上記で説明したフックを gvlayout_neato_layout.c に追加します。
  2. lib/xxxgen に、Makefile.amlib/fdpgen/Makefile.amのような簡単な例に基づいたもの)を提供します。
  3. lib/Makefile.am で、xxxgenSUBDIRS に追加します。
  4. configure.ac で、lib/xxxgen/MakefileAC_CONFIG_FILES に追加します。
  5. lib/plugin/neato_layout/Makefile.am で、$(top_builddir)/lib/xxxgen/libxxxgen_C.lalibgvplugin_neato_layout_C_la_LIBADD に挿入します。
  6. configure だけでは間違った推測をする可能性があるため、autogen.sh を実行することを忘れないでください。

これは、システムにさまざまな automake ツールの適切なバージョンがあることも前提としています。

最終更新日 2022年12月6日: typo を修正 (de77e3d)