Написание плагинов компоновки
Чтобы создать новый плагин компоновки под названием xxx, сначала нужно предоставить две функции: xxx_layout и xxx_cleanup. Их семантика описана ниже.
Компоновка
void xxx_layout(Agraph_t * g)
Инициализируйте граф.
-
Если алгоритм будет использовать общий код маршрутизации рёбер, ему следует вызвать
setEdgeType (g, ...);. -
Для каждого узла вызовите
common_init_nodeиgv_nodesize. -
Если алгоритм будет использовать
spline_edges()для маршрутизации рёбер, координаты узлов необходимо хранить вND_pos, поэтому эту структуру следует выделить здесь. Это, а также два упомянутых выше вызова, выполняется вызовом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(преобразуя дюймы в пункты) и вызывает spline_edges1 с типом ребра, указанным вsetEdgeType(). -
spline_edges (Agraph_t*)Предполагает, что координаты узлов хранятся вND_pos. Эта функция вычисляет ограничивающий прямоугольник g и сохраняет его вGD_bb, а затем вызываетspline_edges0().
Если алгоритм работает только с компонентами связности, код может использовать библиотеку pack для получения компонентов, их отдельной компоновки и последующей упаковки в соответствии с настройками пользователя. Типичная схема приведена ниже. Более подробные примеры можно найти в коде twopi, circo, neato или fdp.
int ncc;
Agraph_t **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 (int i = 0; i < ncc; i++) {
Agraph_t *const 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 (int 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)
{
for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
xxx_init_node(n);
}
for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
for (edge_t *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, поэтому эти вызовы должны выполняться последними. При желании их можно включить в явные вызовы 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)
{
for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
for (Agedge_t *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, а также новое перечисление 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} }; static gvplugin_api_t apis[] = { {API_layout, &gvlayout_xxx_types}, {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. - Разумеется, большинство имён и строк могут быть произвольными. Одно из ограничений: внешний идентификатор типа
gvplugin_library_tдолжен оканчиваться на_LTX_library. Кроме того, строкаxxxв каждой записиgvlayout_xxx_typesиспользуется для идентификации алгоритма компоновки, поэтому она должна отличаться от названий всех остальных алгоритмов компоновки. - В настоящее время возможности алгоритма компоновки задаются битовой маской, и поддерживается только флаг
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. - Не забудьте запустить
autogen.sh, потому что без этогоconfigureможет сделать неверный вывод.
Предполагается также, что в вашей системе установлены подходящие версии различных инструментов automake.
© 2025 The Graphviz Authors
Licensed under the Eclipse Public License 1.0.
https://www.graphviz.org/docs/layouts/writing-layout-plugins/