Graphviz 13.0.0~dev.20241220.2304
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/render.h>
19#include <common/utils.h>
20#include <cgraph/list.h>
21#include <limits.h>
22#include <osage/osage.h>
23#include <neatogen/neatoprocs.h>
24#include <pack/pack.h>
25#include <stdbool.h>
26#include <stddef.h>
27#include <util/alloc.h>
28#include <util/startswith.h>
29
30#define DFLT_SZ 18
31#define PARENT(n) ((Agraph_t*)ND_alg(n))
32
33static void
34indent (int i)
35{
36 for (; i > 0; i--)
37 fputs (" ", stderr);
38}
39
40DEFINE_LIST(clist, Agraph_t*)
41
43{
44 Agnode_t *n;
45 Agedge_t *e;
46
48 Ndim = GD_ndim(g)=2; /* The algorithm only makes sense in 2D */
49
50 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
52 }
53 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
54 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
55 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true); //edge custom data
57 }
58 }
59}
60
61/* layout:
62 */
63static void
64layout (Agraph_t* g, int depth)
65{
66 int i, j, total, nv;
67 int nvs = 0; /* no. of nodes in subclusters */
68 Agnode_t* n;
69 Agraph_t* subg;
70 pointf* pts;
71 boxf bb, rootbb;
72 pointf p;
73 pack_info pinfo;
74 pack_mode pmode;
75 double margin;
76 Agsym_t* cattr = NULL;
77 Agsym_t* vattr = NULL;
78 Agraph_t* root = g->root;
79
80 if (Verbose > 1) {
81 indent (depth);
82 fprintf (stderr, "layout %s\n", agnameof(g));
83 }
84
85 /* Lay out subclusters */
86 for (i = 1; i <= GD_n_cluster(g); i++) {
87 subg = GD_clust(g)[i];
88 layout (subg, depth+1);
89 nvs += agnnodes (subg);
90 }
91
92 nv = agnnodes(g);
93 total = (nv - nvs) + GD_n_cluster(g);
94
95 if ((total == 0) && (GD_label(g) == NULL)) {
96 GD_bb(g).LL.x = GD_bb(g).LL.y = 0;
97 GD_bb(g).UR.x = GD_bb(g).UR.y = DFLT_SZ;
98 return;
99 }
100
101 pmode = getPackInfo(g, l_array, DFLT_MARGIN, &pinfo);
102 if (pmode < l_graph) pinfo.mode = l_graph;
103
104 /* add user sort values if necessary */
105 if ((pinfo.mode == l_array) && (pinfo.flags & PK_USER_VALS)) {
106 cattr = agattr(root, AGRAPH, "sortv", 0);
107 vattr = agattr(root, AGNODE, "sortv", 0);
108 if (cattr || vattr)
109 pinfo.vals = gv_calloc(total, sizeof(packval_t));
110 else
111 agwarningf("Graph %s has array packing with user values but no \"sortv\" attributes are defined.",
112 agnameof(g));
113 }
114
115 boxf *gs = gv_calloc(total, sizeof(boxf));
116 void **children = gv_calloc(total, sizeof(void*));
117 j = 0;
118 for (i = 1; i <= GD_n_cluster(g); i++) {
119 subg = GD_clust(g)[i];
120 gs[j] = GD_bb(subg);
121 if (pinfo.vals && cattr) {
122 pinfo.vals[j] = late_int (subg, cattr, 0, 0);
123 }
124 children[j++] = subg;
125 }
126
127 if (nv-nvs > 0) {
128 for (n = agfstnode (g); n; n = agnxtnode (g,n)) {
129 if (ND_alg(n)) continue;
130 ND_alg(n) = g;
131 bb.LL.y = bb.LL.x = 0;
132 bb.UR.x = ND_xsize(n);
133 bb.UR.y = ND_ysize(n);
134 gs[j] = bb;
135 if (pinfo.vals && vattr) {
136 pinfo.vals[j] = late_int (n, vattr, 0, 0);
137 }
138 children[j++] = n;
139 }
140 }
141
142 /* pack rectangles */
143 assert(total >= 0);
144 pts = putRects((size_t)total, gs, &pinfo);
145 free (pinfo.vals);
146
147 rootbb.LL = (pointf){INT_MAX, INT_MAX};
148 rootbb.UR = (pointf){-INT_MAX, -INT_MAX};
149
150 /* reposition children relative to GD_bb(g) */
151 for (j = 0; j < total; j++) {
152 p = pts[j];
153 bb = gs[j];
154 bb.LL.x += p.x;
155 bb.UR.x += p.x;
156 bb.LL.y += p.y;
157 bb.UR.y += p.y;
158 EXPANDBB(rootbb, bb);
159 if (j < GD_n_cluster(g)) {
160 subg = children[j];
161 GD_bb(subg) = bb;
162 if (Verbose > 1) {
163 indent (depth);
164 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
165 }
166 }
167 else {
168 n = children[j];
169 ND_coord(n) = mid_pointf (bb.LL, bb.UR);
170 if (Verbose > 1) {
171 indent (depth);
172 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
173 }
174 }
175 }
176
177 if (GD_label(g)) {
178 double d;
179
180 pointf pt = GD_label(g)->dimen;
181 if (total == 0) {
182 rootbb.LL.x = 0;
183 rootbb.LL.y = 0;
184 rootbb.UR.x = pt.x;
185 rootbb.UR.y = pt.y;
186
187 }
188 d = pt.x - (rootbb.UR.x - rootbb.LL.x);
189 if (d > 0) { /* height of label is added below */
190 d /= 2;
191 rootbb.LL.x -= d;
192 rootbb.UR.x += d;
193 }
194 }
195
196 if (depth > 0)
197 margin = pinfo.margin/2.0;
198 else
199 margin = 0;
200 rootbb.LL.x -= margin;
201 rootbb.UR.x += margin;
202 rootbb.LL.y -= (margin + GD_border(g)[BOTTOM_IX].y);
203 rootbb.UR.y += (margin + GD_border(g)[TOP_IX].y);
204
205 if (Verbose > 1) {
206 indent (depth);
207 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
208 }
209
210 /* Translate so that rootbb.LL is origin.
211 * This makes the repositioning simpler; we just translate the clusters and nodes in g by
212 * the final bb.ll of g.
213 */
214 for (j = 0; j < total; j++) {
215 if (j < GD_n_cluster(g)) {
216 subg = children[j];
217 bb = GD_bb(subg);
218 bb.LL = sub_pointf(bb.LL, rootbb.LL);
219 bb.UR = sub_pointf(bb.UR, rootbb.LL);
220 GD_bb(subg) = bb;
221 if (Verbose > 1) {
222 indent (depth);
223 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
224 }
225 }
226 else {
227 n = children[j];
228 ND_coord(n) = sub_pointf (ND_coord(n), rootbb.LL);
229 if (Verbose > 1) {
230 indent (depth);
231 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
232 }
233 }
234 }
235
236 rootbb.UR = sub_pointf(rootbb.UR, rootbb.LL);
237 rootbb.LL = sub_pointf(rootbb.LL, rootbb.LL);
238 GD_bb(g) = rootbb;
239
240 if (Verbose > 1) {
241 indent (depth);
242 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
243 }
244
245 free (gs);
246 free (children);
247 free (pts);
248}
249
250static void
251reposition (Agraph_t* g, int depth)
252{
253 boxf sbb, bb = GD_bb(g);
254 Agnode_t* n;
255 Agraph_t* subg;
256 int i;
257
258 if (Verbose > 1) {
259 indent (depth);
260 fprintf (stderr, "reposition %s\n", agnameof(g));
261 }
262
263 /* translate nodes in g but not in a subcluster */
264 if (depth) {
265 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
266 if (PARENT(n) != g)
267 continue;
268 ND_coord(n).x += bb.LL.x;
269 ND_coord(n).y += bb.LL.y;
270 if (Verbose > 1) {
271 indent (depth);
272 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
273 }
274 }
275 }
276
277 /* translate top-level clusters and recurse */
278 for (i = 1; i <= GD_n_cluster(g); i++) {
279 subg = GD_clust(g)[i];
280 if (depth) {
281 sbb = GD_bb(subg);
282 sbb.LL.x += bb.LL.x;
283 sbb.LL.y += bb.LL.y;
284 sbb.UR.x += bb.LL.x;
285 sbb.UR.y += bb.LL.y;
286 if (Verbose > 1) {
287 indent (depth);
288 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), sbb.LL.x, sbb.LL.y, sbb.UR.x, sbb.UR.y);
289 }
290 GD_bb(subg) = sbb;
291 }
292 reposition (subg, depth+1);
293 }
294
295}
296
297static void
298mkClusters (Agraph_t* g, clist_t* pclist, Agraph_t* parent)
299{
300 graph_t* subg;
301 clist_t list = {0};
302 clist_t* clist;
303
304 if (pclist == NULL) {
305 // [0] is empty. The clusters are in [1..cnt].
306 clist_append(&list, NULL);
307 clist = &list;
308 }
309 else
310 clist = pclist;
311
312 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
313 if (is_a_cluster(subg)) {
314 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
315 do_graph_label (subg);
316 clist_append(clist, subg);
317 mkClusters(subg, NULL, subg);
318 }
319 else {
320 mkClusters(subg, clist, parent);
321 }
322 }
323 if (pclist == NULL) {
324 assert(clist_size(&list) - 1 <= INT_MAX);
325 GD_n_cluster(g) = (int)(clist_size(&list) - 1);
326 if (clist_size(&list) > 1) {
327 clist_shrink_to_fit(&list);
328 GD_clust(g) = clist_detach(&list);
329 } else {
330 clist_free(&list);
331 }
332 }
333}
334
336{
338 mkClusters(g, NULL, g);
339 layout(g, 0);
340 reposition (g, 0);
341
342 if (GD_drawing(g)->ratio_kind) {
343 Agnode_t* n;
344 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
345 ND_pos(n)[0] = PS2INCH(ND_coord(n).x);
346 ND_pos(n)[1] = PS2INCH(ND_coord(n).y);
347 }
348 spline_edges0(g, true);
349 }
350 else {
351 int et = EDGE_TYPE (g);
352 if (et != EDGETYPE_NONE) spline_edges1(g, et);
353 }
355}
356
357static void cleanup_graphs (Agraph_t *g)
358{
359 graph_t *subg;
360 int i;
361
362 for (i = 1; i <= GD_n_cluster(g); i++) {
363 subg = GD_clust(g)[i];
364 free_label(GD_label(subg));
365 cleanup_graphs (subg);
366 }
367 free (GD_clust(g));
368}
369
371{
372 node_t *n;
373
374 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
375 for (edge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
377 }
379 }
381}
382
#define DFLT_MARGIN
Definition adjust.h:21
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:80
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1434
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:33
void common_init_edge(edge_t *e)
Definition utils.c:504
bool is_a_cluster(Agraph_t *g)
Definition utils.c:690
#define BOTTOM_IX
Definition const.h:111
#define TOP_IX
Definition const.h:113
#define EDGETYPE_LINE
Definition const.h:249
#define EDGETYPE_NONE
Definition const.h:248
#define PS2INCH(a_points)
Definition geom.h:70
struct pointf_s pointf
#define EXPANDBB(b0, b1)
Definition geom.h:57
static pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:81
static pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:72
unsigned short Ndim
Definition globals.h:61
static bool Verbose
Definition gml2gv.c:23
void free(void *)
node NULL
Definition grammar.y:163
int agnnodes(Agraph_t *g)
Definition graph.c:169
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:338
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
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:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#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:158
@ 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:78
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:83
void do_graph_label(graph_t *sg)
Set characteristics of graph label if it exists.
Definition input.c:829
void free_label(textlabel_t *p)
Definition labels.c:199
#define DEFINE_LIST(name, type)
Definition list.h:26
#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:357
static void indent(int i)
Definition osageinit.c:34
static void mkClusters(Agraph_t *g, clist_t *pclist, Agraph_t *parent)
Definition osageinit.c:298
static void reposition(Agraph_t *g, int depth)
Definition osageinit.c:251
void osage_cleanup(Agraph_t *g)
Definition osageinit.c:370
#define PARENT(n)
Definition osageinit.c:31
static void layout(Agraph_t *g, int depth)
Definition osageinit.c:64
void osage_layout(Agraph_t *g)
Definition osageinit.c:335
static void cluster_init_graph(graph_t *g)
Definition osageinit.c:42
#define DFLT_SZ
Definition osageinit.c:30
pointf * putRects(size_t ng, boxf *bbs, pack_info *pinfo)
Definition pack.c:927
pack_mode getPackInfo(Agraph_t *g, pack_mode dflt, int dfltMargin, pack_info *pinfo)
Definition pack.c:1281
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:1524
void gv_cleanup_node(Agnode_t *n)
Definition utils.c:1536
graph or subgraph
Definition cgraph.h:425
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:637
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