Graphviz 14.1.2~dev.20251213.2040
Loading...
Searching...
No Matches
osageinit.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11
12/* FIX: handle cluster labels */
13/*
14 * Written by Emden R. Gansner
15 */
16
17#include <assert.h>
18#include <common/geomprocs.h>
19#include <common/render.h>
20#include <common/utils.h>
21#include <float.h>
22#include <limits.h>
23#include <osage/osage.h>
24#include <neatogen/neatoprocs.h>
25#include <pack/pack.h>
26#include <stdbool.h>
27#include <stddef.h>
28#include <util/alloc.h>
29#include <util/list.h>
30#include <util/startswith.h>
31
32#define DFLT_SZ 18
33#define PARENT(n) ((Agraph_t*)ND_alg(n))
34
35static void
36indent (int i)
37{
38 for (; i > 0; i--)
39 fputs (" ", stderr);
40}
41
42typedef LIST(Agraph_t *) clist_t;
43
44static void cluster_init_graph(graph_t * g)
45{
46 Agnode_t *n;
47 Agedge_t *e;
48
50 Ndim = GD_ndim(g)=2; /* The algorithm only makes sense in 2D */
51
52 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
54 }
55 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
56 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
57 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //edge custom data
59 }
60 }
61}
62
63/* layout:
64 */
65static void
66layout (Agraph_t* g, int depth)
67{
68 int nvs = 0; /* no. of nodes in subclusters */
69 pack_info pinfo;
70 Agsym_t* cattr = NULL;
71 Agsym_t* vattr = NULL;
72 Agraph_t* root = g->root;
73
74 if (Verbose > 1) {
75 indent (depth);
76 fprintf (stderr, "layout %s\n", agnameof(g));
77 }
78
79 /* Lay out subclusters */
80 for (int i = 1; i <= GD_n_cluster(g); i++) {
81 Agraph_t *const subg = GD_clust(g)[i];
82 layout (subg, depth+1);
83 nvs += agnnodes (subg);
84 }
85
86 const int nv = agnnodes(g);
87 const int total = nv - nvs + GD_n_cluster(g);
88
89 if (total == 0 && GD_label(g) == NULL) {
90 GD_bb(g) = (boxf){.UR = {.x = DFLT_SZ, .y = DFLT_SZ}};
91 return;
92 }
93
94 const pack_mode pmode = getPackInfo(g, l_array, DFLT_MARGIN, &pinfo);
95 if (pmode < l_graph) pinfo.mode = l_graph;
96
97 /* add user sort values if necessary */
98 if (pinfo.mode == l_array && (pinfo.flags & PK_USER_VALS)) {
99 cattr = agattr_text(root, AGRAPH, "sortv", 0);
100 vattr = agattr_text(root, AGNODE, "sortv", 0);
101 if (cattr || vattr)
102 pinfo.vals = gv_calloc(total+1, sizeof(packval_t));
103 else
104 agwarningf("Graph %s has array packing with user values but no \"sortv\" attributes are defined.",
105 agnameof(g));
106 }
107
108 LIST(boxf) gs = {0};
109 LIST(void *) children = {0};
110 int j = 0;
111 for (int i = 1; i <= GD_n_cluster(g); i++) {
112 Agraph_t *const subg = GD_clust(g)[i];
113 LIST_APPEND(&gs, GD_bb(subg));
114 if (pinfo.vals && cattr) {
115 pinfo.vals[j] = (packval_t)late_int(subg, cattr, 0, 0);
116 }
117 LIST_APPEND(&children, subg);
118 ++j;
119 }
120
121 if (nv-nvs > 0) {
122 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
123 if (ND_alg(n)) continue;
124 ND_alg(n) = g;
125 const boxf bb = {.UR = {.x = ND_xsize(n), .y = ND_ysize(n)}};
126 LIST_APPEND(&gs, bb);
127 if (pinfo.vals && vattr) {
128 pinfo.vals[j] = (packval_t)late_int(n, vattr, 0, 0);
129 }
130 LIST_APPEND(&children, n);
131 ++j;
132 }
133 }
134
135 /* pack rectangles */
136 pointf *const pts = putRects(LIST_SIZE(&gs),
137 LIST_IS_EMPTY(&gs) ? NULL : LIST_FRONT(&gs),
138 &pinfo);
139 free (pinfo.vals);
140
141 boxf rootbb = {.LL = {DBL_MAX, DBL_MAX}, .UR = {-DBL_MAX, -DBL_MAX}};
142
143 /* reposition children relative to GD_bb(g) */
144 for (size_t i = 0; i < LIST_SIZE(&gs); ++i) {
145 const pointf p = pts[i];
146 boxf bb = LIST_GET(&gs, i);
147 bb.LL = add_pointf(bb.LL, p);
148 bb.UR = add_pointf(bb.UR, p);
149 EXPANDBB(&rootbb, bb);
150 if (i < (size_t)GD_n_cluster(g)) {
151 Agraph_t *const subg = LIST_GET(&children, i);
152 GD_bb(subg) = bb;
153 if (Verbose > 1) {
154 indent (depth);
155 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
156 }
157 }
158 else {
159 Agnode_t *const n = LIST_GET(&children, i);
160 if (n) {
161 ND_coord(n) = mid_pointf (bb.LL, bb.UR);
162 if (Verbose > 1) {
163 indent (depth);
164 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
165 }
166 }
167 }
168 }
169
170 if (GD_label(g)) {
171 double d;
172
173 pointf pt = GD_label(g)->dimen;
174 if (total == 0) {
175 rootbb = (boxf){.UR = pt};
176 }
177 d = pt.x - (rootbb.UR.x - rootbb.LL.x);
178 if (d > 0) { /* height of label is added below */
179 d /= 2;
180 rootbb.LL.x -= d;
181 rootbb.UR.x += d;
182 }
183 }
184
185 const double margin = depth > 0 ? pinfo.margin / 2.0 : 0;
186 rootbb.LL.x -= margin;
187 rootbb.UR.x += margin;
188 rootbb.LL.y -= margin + GD_border(g)[BOTTOM_IX].y;
189 rootbb.UR.y += margin + GD_border(g)[TOP_IX].y;
190
191 if (Verbose > 1) {
192 indent (depth);
193 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
194 }
195
196 /* Translate so that rootbb.LL is origin.
197 * This makes the repositioning simpler; we just translate the clusters and nodes in g by
198 * the final bb.ll of g.
199 */
200 for (size_t i = 0; i < LIST_SIZE(&children); ++i) {
201 if (i < (size_t)GD_n_cluster(g)) {
202 Agraph_t *const subg = LIST_GET(&children, i);
203 boxf bb = GD_bb(subg);
204 bb.LL = sub_pointf(bb.LL, rootbb.LL);
205 bb.UR = sub_pointf(bb.UR, rootbb.LL);
206 GD_bb(subg) = bb;
207 if (Verbose > 1) {
208 indent (depth);
209 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
210 }
211 }
212 else {
213 Agnode_t *const n = LIST_GET(&children, i);
214 if (n) {
215 ND_coord(n) = sub_pointf (ND_coord(n), rootbb.LL);
216 if (Verbose > 1) {
217 indent (depth);
218 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
219 }
220 }
221 }
222 }
223
224 rootbb = (boxf){.UR = sub_pointf(rootbb.UR, rootbb.LL)};
225 GD_bb(g) = rootbb;
226
227 if (Verbose > 1) {
228 indent (depth);
229 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
230 }
231
232 LIST_FREE(&gs);
233 LIST_FREE(&children);
234 free (pts);
235}
236
237static void
238reposition (Agraph_t* g, int depth)
239{
240 boxf bb = GD_bb(g);
241
242 if (Verbose > 1) {
243 indent (depth);
244 fprintf (stderr, "reposition %s\n", agnameof(g));
245 }
246
247 /* translate nodes in g but not in a subcluster */
248 if (depth) {
249 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
250 if (PARENT(n) != g)
251 continue;
252 ND_coord(n).x += bb.LL.x;
253 ND_coord(n).y += bb.LL.y;
254 if (Verbose > 1) {
255 indent (depth);
256 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
257 }
258 }
259 }
260
261 /* translate top-level clusters and recurse */
262 for (int i = 1; i <= GD_n_cluster(g); i++) {
263 Agraph_t *const subg = GD_clust(g)[i];
264 if (depth) {
265 boxf sbb = GD_bb(subg);
266 sbb.LL.x += bb.LL.x;
267 sbb.LL.y += bb.LL.y;
268 sbb.UR.x += bb.LL.x;
269 sbb.UR.y += bb.LL.y;
270 if (Verbose > 1) {
271 indent (depth);
272 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), sbb.LL.x, sbb.LL.y, sbb.UR.x, sbb.UR.y);
273 }
274 GD_bb(subg) = sbb;
275 }
276 reposition (subg, depth+1);
277 }
278
279}
280
281static void
282mkClusters (Agraph_t* g, clist_t* pclist, Agraph_t* parent)
283{
284 clist_t list = {0};
285 clist_t* clist;
286
287 if (pclist == NULL) {
288 // [0] is empty. The clusters are in [1..cnt].
289 LIST_APPEND(&list, NULL);
290 clist = &list;
291 }
292 else
293 clist = pclist;
294
295 for (graph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
296 if (is_a_cluster(subg)) {
297 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
298 do_graph_label (subg);
299 LIST_APPEND(clist, subg);
300 mkClusters(subg, NULL, subg);
301 }
302 else {
303 mkClusters(subg, clist, parent);
304 }
305 }
306 if (pclist == NULL) {
307 assert(LIST_SIZE(&list) - 1 <= INT_MAX);
308 GD_n_cluster(g) = (int)(LIST_SIZE(&list) - 1);
309 if (LIST_SIZE(&list) > 1) {
310 LIST_SHRINK_TO_FIT(&list);
311 LIST_DETACH(&list, &GD_clust(g), NULL);
312 } else {
313 LIST_FREE(&list);
314 }
315 }
316}
317
319{
320 cluster_init_graph(g);
321 mkClusters(g, NULL, g);
322 layout(g, 0);
323 reposition (g, 0);
324
325 if (GD_drawing(g)->ratio_kind) {
326 Agnode_t* n;
327 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
328 ND_pos(n)[0] = PS2INCH(ND_coord(n).x);
329 ND_pos(n)[1] = PS2INCH(ND_coord(n).y);
330 }
331 spline_edges0(g, true);
332 }
333 else {
334 int et = EDGE_TYPE (g);
335 if (et != EDGETYPE_NONE) spline_edges1(g, et);
336 }
338}
339
340static void cleanup_graphs (Agraph_t *g)
341{
342 for (int i = 1; i <= GD_n_cluster(g); i++) {
343 graph_t *const subg = GD_clust(g)[i];
344 free_label(GD_label(subg));
345 cleanup_graphs (subg);
346 }
347 free (GD_clust(g));
348}
349
351{
352 for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
353 for (edge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
355 }
357 }
359}
360
#define DFLT_MARGIN
Definition adjust.h:23
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define parent(i)
Definition closest.c:73
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1420
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:36
void common_init_edge(edge_t *e)
Definition utils.c:507
bool is_a_cluster(Agraph_t *g)
Definition utils.c:693
#define BOTTOM_IX
Definition const.h:111
#define TOP_IX
Definition const.h:113
#define EDGETYPE_LINE
Definition const.h:235
#define EDGETYPE_NONE
Definition const.h:234
#define PS2INCH(a_points)
Definition geom.h:64
geometric functions (e.g. on points and boxes)
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
#define EXPANDBB(b0, b1)
Definition geomprocs.h:65
unsigned short Ndim
Definition globals.h:62
static bool Verbose
Definition gml2gv.c:24
void free(void *)
node NULL
Definition grammar.y:181
int agnnodes(Agraph_t *g)
Definition graph.c:155
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:334
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:26
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:41
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define GD_drawing(g)
Definition types.h:353
#define GD_border(g)
Definition types.h:359
#define GD_clust(g)
Definition types.h:360
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_ndim(g)
Definition types.h:390
#define GD_label(g)
Definition types.h:374
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:48
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:41
#define ND_ysize(n)
Definition types.h:538
#define ND_alg(n)
Definition types.h:484
#define ND_pos(n)
Definition types.h:520
#define ND_coord(n)
Definition types.h:490
#define ND_xsize(n)
Definition types.h:537
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:143
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:89
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:73
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:78
void do_graph_label(graph_t *sg)
Set characteristics of graph label if it exists.
Definition input.c:834
void free_label(textlabel_t *p)
Definition labels.c:202
type-generic dynamically expanding list
#define LIST_DETACH(list, datap, sizep)
Definition list.h:443
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_SHRINK_TO_FIT(list)
Definition list.h:358
#define LIST_FRONT(list)
Definition list.h:180
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_GET(list, index)
Definition list.h:155
#define EDGE_TYPE(g)
Definition macros.h:25
void neato_init_node(node_t *n)
Definition neatoinit.c:59
NEATOPROCS_API void spline_edges0(Agraph_t *, bool)
NEATOPROCS_API int spline_edges1(graph_t *g, int)
static void cleanup_graphs(Agraph_t *g)
Definition osageinit.c:340
static void indent(int i)
Definition osageinit.c:36
static void mkClusters(Agraph_t *g, clist_t *pclist, Agraph_t *parent)
Definition osageinit.c:282
static void reposition(Agraph_t *g, int depth)
Definition osageinit.c:238
void osage_cleanup(Agraph_t *g)
Definition osageinit.c:350
#define PARENT(n)
Definition osageinit.c:33
static void layout(Agraph_t *g, int depth)
Definition osageinit.c:66
void osage_layout(Agraph_t *g)
Definition osageinit.c:318
#define DFLT_SZ
Definition osageinit.c:32
pointf * putRects(size_t ng, boxf *bbs, pack_info *pinfo)
Definition pack.c:928
pack_mode getPackInfo(Agraph_t *g, pack_mode dflt, int dfltMargin, pack_info *pinfo)
Definition pack.c:1282
support for connected components
pack_mode
Definition pack.h:55
@ l_graph
Definition pack.h:55
@ l_array
Definition pack.h:55
unsigned int packval_t
Definition pack.h:65
#define PK_USER_VALS
Definition pack.h:58
void dotneato_postprocess(Agraph_t *g)
Definition postproc.c:689
void gv_cleanup_edge(Agedge_t *e)
Definition utils.c:1510
void gv_cleanup_node(Agnode_t *n)
Definition utils.c:1522
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
int flags
Definition pack.h:75
pack_mode mode
Definition pack.h:72
packval_t * vals
Definition pack.h:74
unsigned int margin
Definition pack.h:70
double x
Definition geom.h:29
double y
Definition geom.h:29