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