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