/** * @file * @brief graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t * @ingroup public_apis * @ingroup common_render */ /************************************************************************* * Copyright (c) 2011 AT&T Intellectual Property * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v10.html * * Contributors: Details at https://graphviz.org *************************************************************************/ #pragma once /* Define if you want CGRAPH */ #define WITH_CGRAPH 1 #include #include #include #include #include #include #include "geom.h" #include "gvcext.h" #include "pathgeom.h" #include "textspan.h" #include "cgraph.h" #ifdef __cplusplus extern "C" { #endif typedef struct Agraph_s graph_t; typedef struct Agnode_s node_t; typedef struct Agedge_s edge_t; typedef struct Agsym_s attrsym_t; #define TAIL_ID "tailport" #define HEAD_ID "headport" typedef struct htmllabel_t htmllabel_t; typedef struct port { /* internal edge endpoint specification */ pointf p; /* aiming point relative to node center */ double theta; /* slope in radians */ boxf *bp; /* if not null, points to bbox of * rectangular area that is port target */ bool defined; /* if true, edge has port info at this end */ bool constrained; /* if true, constraints such as theta are set */ bool clip; /* if true, clip end to node/port shape */ bool dyna; /* if true, assign compass point dynamically */ unsigned char order; /* for mincross */ unsigned char side; /* if port is on perimeter of node, this * contains the bitwise OR of the sides (TOP, * BOTTOM, etc.) it is on. */ char *name; /* port name, if it was explicitly given, otherwise NULL */ } port; typedef struct { bool(*swapEnds) (edge_t * e); /* Should head and tail be swapped? */ bool(*splineMerge) (node_t * n); /* Is n a node in the middle of an edge? */ bool ignoreSwap; /* Test for swapped edges if false */ bool isOrtho; /* Orthogonal routing used */ } splineInfo; typedef struct pathend_t { boxf nb; /* the node box */ pointf np; /* node port */ int sidemask; int boxn; boxf boxes[20]; } pathend_t; typedef struct path { /* internal specification for an edge spline */ port start; port end; size_t nbox; ///< number of subdivisions boxf *boxes; /* rectangular regions of subdivision */ void *data; } path; typedef struct bezier { pointf *list; size_t size; uint32_t sflag; uint32_t eflag; pointf sp; pointf ep; } bezier; typedef struct splines { bezier *list; size_t size; boxf bb; } splines; typedef struct textlabel_t { char *text; char *fontname; char *fontcolor; int charset; double fontsize; pointf dimen; /* the diagonal size of the label (estimated by layout) */ pointf space; /* the diagonal size of the space for the label */ /* the rendered label is aligned in this box */ /* space does not include pad or margin */ pointf pos; /* the center of the space for the label */ union { struct { textspan_t *span; size_t nspans; } txt; htmllabel_t *html; } u; char valign; /* 't' 'c' 'b' */ bool set; /* true if position is set */ bool html; /* true if html label */ } textlabel_t; typedef struct { bool filled: 1; bool radial: 1; bool rounded: 1; bool diagonals: 1; bool auxlabels: 1; bool invisible: 1; bool striped: 1; bool dotted: 1; bool dashed: 1; bool wedged: 1; bool underline: 1; bool fixedshape: 1; unsigned shape: 7; } graphviz_polygon_style_t; typedef struct polygon_t { /* mutable shape information for a node */ int regular; /* true for symmetric shapes */ size_t peripheries; ///< number of periphery lines size_t sides; ///< number of sides double orientation; /* orientation of shape (+ve degrees) */ double distortion; /* distortion factor - as in trapezium */ double skew; /* skew factor - as in parallelogram */ graphviz_polygon_style_t option; ///< ROUNDED, DIAGONAL corners, etc. pointf *vertices; /* array of vertex points */ } polygon_t; typedef union inside_t { struct { pointf *p; double *r; } a; struct { node_t *n; boxf *bp; node_t *lastn; ///< last node argument double radius; ///< last radius seen polygon_t *last_poly; ///< last seen polygon size_t last; ///< last used polygon vertex size_t outp; ///< last used outline periphery double scalex, scaley, box_URx, box_URy; ///< various computed sizes of aspects of the last seen polygon } s; } inside_t; typedef struct stroke_t { /* information about a single stroke */ /* we would have called it a path if that term wasn't already used */ size_t nvertices; ///< number of points in the stroke pointf *vertices; /* array of vertex points */ } stroke_t; typedef struct shape_functions { /* read-only shape functions */ void (*initfn) (node_t *); /* initializes shape from node u.shape_info structure */ void (*freefn) (node_t *); /* frees shape from node u.shape_info structure */ port(*portfn) (node_t *, char *, char *); /* finds aiming point and slope of port */ bool(*insidefn) (inside_t * inside_context, pointf); /* clips incident gvc->e spline on shape of gvc->n */ int (*pboxfn)(node_t* n, port* p, int side, boxf rv[], int *kptr); /* finds box path to reach port */ void (*codefn) (GVJ_t * job, node_t * n); /* emits graphics code for node */ } shape_functions; typedef enum { SH_UNSET, SH_POLY, SH_RECORD, SH_POINT, SH_EPSF} shape_kind; typedef struct shape_desc { /* read-only shape descriptor */ char *name; /* as read from graph file */ shape_functions *fns; polygon_t *polygon; /* base polygon info */ bool usershape; } shape_desc; #include "usershape.h" /* usershapes needed by gvc */ typedef struct adjmatrix_t adjmatrix_t; typedef struct rank_t { int n; /* number of nodes in this rank */ node_t **v; /* ordered list of nodes in rank */ int an; /* globally allocated number of nodes */ node_t **av; /* allocated list of nodes in rank */ double ht1; /* height below/above centerline */ double ht2; /* height below/above centerline */ double pht1; /* as above, but only primitive nodes */ double pht2; /* as above, but only primitive nodes */ bool candidate; /* for transpose () */ bool valid; int cache_nc; /* caches number of crossings */ adjmatrix_t *flat; } rank_t; typedef enum { R_NONE = 0, R_VALUE, R_FILL, R_COMPRESS, R_AUTO, R_EXPAND } ratio_t; typedef struct layout_t { double quantum; double scale; double ratio; /* set only if ratio_kind == R_VALUE */ double dpi; pointf margin; pointf page; pointf size; bool filled; bool landscape; bool centered; ratio_t ratio_kind; void* xdots; char* id; } layout_t; /* for "record" shapes */ typedef struct field_t { pointf size; /* its dimension */ boxf b; /* its placement in node's coordinates */ int n_flds; textlabel_t *lp; /* n_flds == 0 */ struct field_t **fld; /* n_flds > 0 */ char *id; /* user's identifier */ unsigned char LR; /* if box list is horizontal (left to right) */ unsigned char sides; /* sides of node exposed to field */ } field_t; typedef struct nlist_t { node_t **list; size_t size; } nlist_t; typedef struct elist { edge_t **list; size_t size; } elist; #define GUI_STATE_ACTIVE (1<<0) #define GUI_STATE_SELECTED (1<<1) #define GUI_STATE_VISITED (1<<2) #define GUI_STATE_DELETED (1<<3) #define elist_append(item, L) \ do { \ L.list = gv_recalloc(L.list, L.size + 1, L.size + 2, sizeof(edge_t *)); \ L.list[L.size++] = item; \ L.list[L.size] = NULL; \ } while (0) #define alloc_elist(n, L) \ do { \ L.size = 0; \ L.list = gv_calloc(n + 1, sizeof(edge_t *)); \ } while (0) #define free_list(L) free(L.list) typedef enum {NATIVEFONTS,PSFONTS,SVGFONTS} fontname_kind; /// @addtogroup cgraph_graph /// @{ typedef struct Agraphinfo_t { Agrec_t hdr; /* to generate code */ layout_t *drawing; textlabel_t *label; /* if the cluster has a title */ boxf bb; /* bounding box */ pointf border[4]; /* sizes of margins for graph labels */ unsigned char gui_state; /* Graph state for GUI ops */ unsigned char has_labels; bool has_images; unsigned char charset; /* input character set */ int rankdir; double ht1; /* below and above extremal ranks */ double ht2; /* below and above extremal ranks */ unsigned short flags; void *alg; GVC_t *gvc; /* context for "globals" over multiple graphs */ void (*cleanup) (graph_t * g); /* function to deallocate layout-specific data */ #ifndef DOT_ONLY /* to place nodes */ node_t **neato_nlist; int move; double **dist; double **spring; double **sum_t; double ***t; unsigned short ndim; unsigned short odim; #endif #ifndef NEATO_ONLY /* to have subgraphs */ int n_cluster; graph_t **clust; /* clusters are in clust[1..n_cluster] !!! */ graph_t *dotroot; node_t *nlist; rank_t *rank; graph_t *parent; /* containing cluster (not parent subgraph) */ int level; /* cluster nesting level (not node level!) */ node_t *minrep; /* set leaders for min and max rank */ node_t *maxrep; /* set leaders for min and max rank */ /* fast graph node list */ nlist_t comp; /* connected components */ node_t *minset; /* set leaders */ node_t *maxset; /* set leaders */ /* includes virtual */ int minrank; int maxrank; /* various flags */ bool has_flat_edges; unsigned char showboxes; fontname_kind fontnames; /* to override mangling in SVG */ int nodesep; int ranksep; node_t *ln; /* left nodes of bounding box */ node_t *rn; /* right nodes of bounding box */ /* for clusters */ node_t *leader; node_t **rankleader; bool expanded; char installed; char set_type; char label_pos; bool exact_ranksep; #endif } Agraphinfo_t; #define GD_parent(g) (((Agraphinfo_t*)AGDATA(g))->parent) #define GD_level(g) (((Agraphinfo_t*)AGDATA(g))->level) #define GD_drawing(g) (((Agraphinfo_t*)AGDATA(g))->drawing) #define GD_bb(g) (((Agraphinfo_t*)AGDATA(g))->bb) #define GD_gvc(g) (((Agraphinfo_t*)AGDATA(g))->gvc) #define GD_cleanup(g) (((Agraphinfo_t*)AGDATA(g))->cleanup) #define GD_dist(g) (((Agraphinfo_t*)AGDATA(g))->dist) #define GD_alg(g) (((Agraphinfo_t*)AGDATA(g))->alg) #define GD_border(g) (((Agraphinfo_t*)AGDATA(g))->border) #define GD_clust(g) (((Agraphinfo_t*)AGDATA(g))->clust) #define GD_dotroot(g) (((Agraphinfo_t*)AGDATA(g))->dotroot) #define GD_comp(g) (((Agraphinfo_t*)AGDATA(g))->comp) #define GD_exact_ranksep(g) (((Agraphinfo_t*)AGDATA(g))->exact_ranksep) #define GD_expanded(g) (((Agraphinfo_t*)AGDATA(g))->expanded) #define GD_flags(g) (((Agraphinfo_t*)AGDATA(g))->flags) #define GD_gui_state(g) (((Agraphinfo_t*)AGDATA(g))->gui_state) #define GD_charset(g) (((Agraphinfo_t*)AGDATA(g))->charset) #define GD_has_labels(g) (((Agraphinfo_t*)AGDATA(g))->has_labels) #define GD_has_images(g) (((Agraphinfo_t*)AGDATA(g))->has_images) #define GD_has_flat_edges(g) (((Agraphinfo_t*)AGDATA(g))->has_flat_edges) #define GD_ht1(g) (((Agraphinfo_t*)AGDATA(g))->ht1) #define GD_ht2(g) (((Agraphinfo_t*)AGDATA(g))->ht2) #define GD_installed(g) (((Agraphinfo_t*)AGDATA(g))->installed) #define GD_label(g) (((Agraphinfo_t*)AGDATA(g))->label) #define GD_leader(g) (((Agraphinfo_t*)AGDATA(g))->leader) #define GD_rankdir2(g) (((Agraphinfo_t*)AGDATA(g))->rankdir) #define GD_rankdir(g) (((Agraphinfo_t*)AGDATA(g))->rankdir & 0x3) #define GD_flip(g) (GD_rankdir(g) & 1) #define GD_realrankdir(g) ((((Agraphinfo_t*)AGDATA(g))->rankdir) >> 2) #define GD_realflip(g) (GD_realrankdir(g) & 1) #define GD_ln(g) (((Agraphinfo_t*)AGDATA(g))->ln) #define GD_maxrank(g) (((Agraphinfo_t*)AGDATA(g))->maxrank) #define GD_maxset(g) (((Agraphinfo_t*)AGDATA(g))->maxset) #define GD_minrank(g) (((Agraphinfo_t*)AGDATA(g))->minrank) #define GD_minset(g) (((Agraphinfo_t*)AGDATA(g))->minset) #define GD_minrep(g) (((Agraphinfo_t*)AGDATA(g))->minrep) #define GD_maxrep(g) (((Agraphinfo_t*)AGDATA(g))->maxrep) #define GD_move(g) (((Agraphinfo_t*)AGDATA(g))->move) #define GD_n_cluster(g) (((Agraphinfo_t*)AGDATA(g))->n_cluster) #define GD_ndim(g) (((Agraphinfo_t*)AGDATA(g))->ndim) #define GD_odim(g) (((Agraphinfo_t*)AGDATA(g))->odim) #define GD_neato_nlist(g) (((Agraphinfo_t*)AGDATA(g))->neato_nlist) #define GD_nlist(g) (((Agraphinfo_t*)AGDATA(g))->nlist) #define GD_nodesep(g) (((Agraphinfo_t*)AGDATA(g))->nodesep) #define GD_rank(g) (((Agraphinfo_t*)AGDATA(g))->rank) #define GD_rankleader(g) (((Agraphinfo_t*)AGDATA(g))->rankleader) #define GD_ranksep(g) (((Agraphinfo_t*)AGDATA(g))->ranksep) #define GD_rn(g) (((Agraphinfo_t*)AGDATA(g))->rn) #define GD_set_type(g) (((Agraphinfo_t*)AGDATA(g))->set_type) #define GD_label_pos(g) (((Agraphinfo_t*)AGDATA(g))->label_pos) #define GD_showboxes(g) (((Agraphinfo_t*)AGDATA(g))->showboxes) #define GD_fontnames(g) (((Agraphinfo_t*)AGDATA(g))->fontnames) #define GD_spring(g) (((Agraphinfo_t*)AGDATA(g))->spring) #define GD_sum_t(g) (((Agraphinfo_t*)AGDATA(g))->sum_t) #define GD_t(g) (((Agraphinfo_t*)AGDATA(g))->t) /// @} /// @addtogroup cgraph_node /// @{ typedef struct Agnodeinfo_t { Agrec_t hdr; shape_desc *shape; void *shape_info; pointf coord; double width; /* inches */ double height; /* inches */ boxf bb; double ht; double lw; double rw; double outline_width; /* width in points with penwidth taken into account */ double outline_height; /* height in points with penwidth taken into account */ textlabel_t *label; textlabel_t *xlabel; void *alg; char state; unsigned char gui_state; /* Node state for GUI ops */ bool clustnode; #ifndef DOT_ONLY unsigned char pinned; int id; int heapindex; int hops; double *pos; double dist; #endif #ifndef NEATO_ONLY unsigned char showboxes; bool has_port; node_t* rep; node_t *set; /* fast graph */ char node_type; size_t mark; char onstack; char ranktype; char weight_class; node_t *next; node_t *prev; elist in; elist out; elist flat_out; elist flat_in; elist other; graph_t *clust; /* for union-find and collapsing nodes */ int UF_size; node_t *UF_parent; /* for placing nodes */ int rank; int order; /* initially, order = 1 for ordered edges */ double mval; elist save_in; elist save_out; /* for network-simplex */ elist tree_in; elist tree_out; edge_t *par; int low; int lim; int priority; double pad[1]; #endif } Agnodeinfo_t; #define ND_id(n) (((Agnodeinfo_t*)AGDATA(n))->id) #define ND_alg(n) (((Agnodeinfo_t*)AGDATA(n))->alg) #define ND_UF_parent(n) (((Agnodeinfo_t*)AGDATA(n))->UF_parent) #define ND_set(n) (((Agnodeinfo_t*)AGDATA(n))->set) #define ND_UF_size(n) (((Agnodeinfo_t*)AGDATA(n))->UF_size) #define ND_bb(n) (((Agnodeinfo_t*)AGDATA(n))->bb) #define ND_clust(n) (((Agnodeinfo_t*)AGDATA(n))->clust) #define ND_coord(n) (((Agnodeinfo_t*)AGDATA(n))->coord) #define ND_dist(n) (((Agnodeinfo_t*)AGDATA(n))->dist) #define ND_flat_in(n) (((Agnodeinfo_t*)AGDATA(n))->flat_in) #define ND_flat_out(n) (((Agnodeinfo_t*)AGDATA(n))->flat_out) #define ND_gui_state(n) (((Agnodeinfo_t*)AGDATA(n))->gui_state) #define ND_has_port(n) (((Agnodeinfo_t*)AGDATA(n))->has_port) #define ND_rep(n) (((Agnodeinfo_t*)AGDATA(n))->rep) #define ND_heapindex(n) (((Agnodeinfo_t*)AGDATA(n))->heapindex) #define ND_height(n) (((Agnodeinfo_t*)AGDATA(n))->height) #define ND_hops(n) (((Agnodeinfo_t*)AGDATA(n))->hops) #define ND_ht(n) (((Agnodeinfo_t*)AGDATA(n))->ht) #define ND_in(n) (((Agnodeinfo_t*)AGDATA(n))->in) #define ND_label(n) (((Agnodeinfo_t*)AGDATA(n))->label) #define ND_xlabel(n) (((Agnodeinfo_t*)AGDATA(n))->xlabel) #define ND_lim(n) (((Agnodeinfo_t*)AGDATA(n))->lim) #define ND_low(n) (((Agnodeinfo_t*)AGDATA(n))->low) #define ND_lw(n) (((Agnodeinfo_t*)AGDATA(n))->lw) #define ND_mark(n) (((Agnodeinfo_t*)AGDATA(n))->mark) #define ND_mval(n) (((Agnodeinfo_t*)AGDATA(n))->mval) #define ND_n_cluster(n) (((Agnodeinfo_t*)AGDATA(n))->n_cluster) #define ND_next(n) (((Agnodeinfo_t*)AGDATA(n))->next) #define ND_node_type(n) (((Agnodeinfo_t*)AGDATA(n))->node_type) #define ND_onstack(n) (((Agnodeinfo_t*)AGDATA(n))->onstack) #define ND_order(n) (((Agnodeinfo_t*)AGDATA(n))->order) #define ND_other(n) (((Agnodeinfo_t*)AGDATA(n))->other) #define ND_out(n) (((Agnodeinfo_t*)AGDATA(n))->out) #define ND_outline_width(n) (((Agnodeinfo_t*)AGDATA(n))->outline_width) #define ND_outline_height(n) (((Agnodeinfo_t*)AGDATA(n))->outline_height) #define ND_par(n) (((Agnodeinfo_t*)AGDATA(n))->par) #define ND_pinned(n) (((Agnodeinfo_t*)AGDATA(n))->pinned) #define ND_pos(n) (((Agnodeinfo_t*)AGDATA(n))->pos) #define ND_prev(n) (((Agnodeinfo_t*)AGDATA(n))->prev) #define ND_priority(n) (((Agnodeinfo_t*)AGDATA(n))->priority) #define ND_rank(n) (((Agnodeinfo_t*)AGDATA(n))->rank) #define ND_ranktype(n) (((Agnodeinfo_t*)AGDATA(n))->ranktype) #define ND_rw(n) (((Agnodeinfo_t*)AGDATA(n))->rw) #define ND_save_in(n) (((Agnodeinfo_t*)AGDATA(n))->save_in) #define ND_save_out(n) (((Agnodeinfo_t*)AGDATA(n))->save_out) #define ND_shape(n) (((Agnodeinfo_t*)AGDATA(n))->shape) #define ND_shape_info(n) (((Agnodeinfo_t*)AGDATA(n))->shape_info) #define ND_showboxes(n) (((Agnodeinfo_t*)AGDATA(n))->showboxes) #define ND_state(n) (((Agnodeinfo_t*)AGDATA(n))->state) #define ND_clustnode(n) (((Agnodeinfo_t*)AGDATA(n))->clustnode) #define ND_tree_in(n) (((Agnodeinfo_t*)AGDATA(n))->tree_in) #define ND_tree_out(n) (((Agnodeinfo_t*)AGDATA(n))->tree_out) #define ND_weight_class(n) (((Agnodeinfo_t*)AGDATA(n))->weight_class) #define ND_width(n) (((Agnodeinfo_t*)AGDATA(n))->width) #define ND_xsize(n) (ND_lw(n)+ND_rw(n)) #define ND_ysize(n) (ND_ht(n)) /// @} /// @addtogroup cgraph_edge /// @{ typedef struct Agedgeinfo_t { Agrec_t hdr; splines *spl; port tail_port; port head_port; textlabel_t *label; textlabel_t *head_label; textlabel_t *tail_label; textlabel_t *xlabel; char edge_type; char compound; char adjacent; /* true for flat edge with adjacent nodes */ char label_ontop; unsigned char gui_state; /* Edge state for GUI ops */ edge_t *to_orig; /* for dot's shapes.c */ void *alg; #ifndef DOT_ONLY double factor; double dist; Ppolyline_t path; #endif #ifndef NEATO_ONLY unsigned char showboxes; bool conc_opp_flag; short xpenalty; int weight; int cutvalue; int tree_index; short count; int minlen; edge_t *to_virt; #endif } Agedgeinfo_t; #define ED_alg(e) (((Agedgeinfo_t*)AGDATA(e))->alg) #define ED_conc_opp_flag(e) (((Agedgeinfo_t*)AGDATA(e))->conc_opp_flag) #define ED_count(e) (((Agedgeinfo_t*)AGDATA(e))->count) #define ED_cutvalue(e) (((Agedgeinfo_t*)AGDATA(e))->cutvalue) #define ED_edge_type(e) (((Agedgeinfo_t*)AGDATA(e))->edge_type) #define ED_compound(e) (((Agedgeinfo_t*)AGDATA(e))->compound) #define ED_adjacent(e) (((Agedgeinfo_t*)AGDATA(e))->adjacent) #define ED_factor(e) (((Agedgeinfo_t*)AGDATA(e))->factor) #define ED_gui_state(e) (((Agedgeinfo_t*)AGDATA(e))->gui_state) #define ED_head_label(e) (((Agedgeinfo_t*)AGDATA(e))->head_label) #define ED_head_port(e) (((Agedgeinfo_t*)AGDATA(e))->head_port) #define ED_label(e) (((Agedgeinfo_t*)AGDATA(e))->label) #define ED_xlabel(e) (((Agedgeinfo_t*)AGDATA(e))->xlabel) #define ED_label_ontop(e) (((Agedgeinfo_t*)AGDATA(e))->label_ontop) #define ED_minlen(e) (((Agedgeinfo_t*)AGDATA(e))->minlen) #define ED_path(e) (((Agedgeinfo_t*)AGDATA(e))->path) #define ED_showboxes(e) (((Agedgeinfo_t*)AGDATA(e))->showboxes) #define ED_spl(e) (((Agedgeinfo_t*)AGDATA(e))->spl) #define ED_tail_label(e) (((Agedgeinfo_t*)AGDATA(e))->tail_label) #define ED_tail_port(e) (((Agedgeinfo_t*)AGDATA(e))->tail_port) #define ED_to_orig(e) (((Agedgeinfo_t*)AGDATA(e))->to_orig) #define ED_to_virt(e) (((Agedgeinfo_t*)AGDATA(e))->to_virt) #define ED_tree_index(e) (((Agedgeinfo_t*)AGDATA(e))->tree_index) #define ED_xpenalty(e) (((Agedgeinfo_t*)AGDATA(e))->xpenalty) #define ED_dist(e) (((Agedgeinfo_t*)AGDATA(e))->dist) #define ED_weight(e) (((Agedgeinfo_t*)AGDATA(e))->weight) /// @} #define ag_xget(x,a) agxget(x,a) #define SET_RANKDIR(g,rd) (GD_rankdir2(g) = rd) /// @ingroup cgraph_edge #define agfindedge(g,t,h) (agedge(g,t,h,NULL,0)) /// @ingroup cgraph_node #define agfindnode(g,n) (agnode(g,n,0)) /// @ingroup cgraph_graph #define agfindgraphattr(g,a) (agattr(g,AGRAPH,a,NULL)) /// @ingroup cgraph_node #define agfindnodeattr(g,a) (agattr(g,AGNODE,a,NULL)) /// @ingroup cgraph_edge #define agfindedgeattr(g,a) (agattr(g,AGEDGE,a,NULL)) typedef struct { int flags; } gvlayout_features_t; #ifdef __cplusplus } #endif /// @defgroup public_apis Graphviz public APIs