Graphviz 14.1.2~dev.20251231.1527
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 == NULL && vattr == NULL)
102 agwarningf("Graph %s has array packing with user values but no \"sortv\" attributes are defined.",
103 agnameof(g));
104 }
105
106 LIST(boxf) gs = {0};
107 LIST(void *) children = {0};
108 LIST(packval_t) vals = {0};
109 for (int i = 1; i <= GD_n_cluster(g); i++) {
110 Agraph_t *const subg = GD_clust(g)[i];
111 LIST_APPEND(&gs, GD_bb(subg));
112 if (cattr) {
113 LIST_APPEND(&vals, (packval_t)late_int(subg, cattr, 0, 0));
114 }
115 LIST_APPEND(&children, subg);
116 }
117
118 if (nv-nvs > 0) {
119 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
120 if (ND_alg(n)) continue;
121 ND_alg(n) = g;
122 const boxf bb = {.UR = {.x = ND_xsize(n), .y = ND_ysize(n)}};
123 LIST_APPEND(&gs, bb);
124 if (vattr) {
125 LIST_APPEND(&vals, (packval_t)late_int(n, vattr, 0, 0));
126 }
127 LIST_APPEND(&children, n);
128 }
129 }
130
131 /* pack rectangles */
132 pinfo.vals = LIST_IS_EMPTY(&vals) ? NULL : LIST_FRONT(&vals);
133 pointf *const pts = putRects(LIST_SIZE(&gs),
134 LIST_IS_EMPTY(&gs) ? NULL : LIST_FRONT(&gs),
135 &pinfo);
136
137 boxf rootbb = {.LL = {DBL_MAX, DBL_MAX}, .UR = {-DBL_MAX, -DBL_MAX}};
138
139 /* reposition children relative to GD_bb(g) */
140 for (size_t i = 0; i < LIST_SIZE(&gs); ++i) {
141 const pointf p = pts[i];
142 boxf bb = LIST_GET(&gs, i);
143 bb.LL = add_pointf(bb.LL, p);
144 bb.UR = add_pointf(bb.UR, p);
145 EXPANDBB(&rootbb, bb);
146 if (i < (size_t)GD_n_cluster(g)) {
147 Agraph_t *const subg = LIST_GET(&children, i);
148 GD_bb(subg) = bb;
149 if (Verbose > 1) {
150 indent (depth);
151 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
152 }
153 }
154 else {
155 Agnode_t *const n = LIST_GET(&children, i);
156 if (n) {
157 ND_coord(n) = mid_pointf (bb.LL, bb.UR);
158 if (Verbose > 1) {
159 indent (depth);
160 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
161 }
162 }
163 }
164 }
165
166 if (GD_label(g)) {
167 double d;
168
169 pointf pt = GD_label(g)->dimen;
170 if (total == 0) {
171 rootbb = (boxf){.UR = pt};
172 }
173 d = pt.x - (rootbb.UR.x - rootbb.LL.x);
174 if (d > 0) { /* height of label is added below */
175 d /= 2;
176 rootbb.LL.x -= d;
177 rootbb.UR.x += d;
178 }
179 }
180
181 const double margin = depth > 0 ? pinfo.margin / 2.0 : 0;
182 rootbb.LL.x -= margin;
183 rootbb.UR.x += margin;
184 rootbb.LL.y -= margin + GD_border(g)[BOTTOM_IX].y;
185 rootbb.UR.y += margin + GD_border(g)[TOP_IX].y;
186
187 if (Verbose > 1) {
188 indent (depth);
189 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
190 }
191
192 /* Translate so that rootbb.LL is origin.
193 * This makes the repositioning simpler; we just translate the clusters and nodes in g by
194 * the final bb.ll of g.
195 */
196 for (size_t i = 0; i < LIST_SIZE(&children); ++i) {
197 if (i < (size_t)GD_n_cluster(g)) {
198 Agraph_t *const subg = LIST_GET(&children, i);
199 boxf bb = GD_bb(subg);
200 bb.LL = sub_pointf(bb.LL, rootbb.LL);
201 bb.UR = sub_pointf(bb.UR, rootbb.LL);
202 GD_bb(subg) = bb;
203 if (Verbose > 1) {
204 indent (depth);
205 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
206 }
207 }
208 else {
209 Agnode_t *const n = LIST_GET(&children, i);
210 if (n) {
211 ND_coord(n) = sub_pointf (ND_coord(n), rootbb.LL);
212 if (Verbose > 1) {
213 indent (depth);
214 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
215 }
216 }
217 }
218 }
219
220 rootbb = (boxf){.UR = sub_pointf(rootbb.UR, rootbb.LL)};
221 GD_bb(g) = rootbb;
222
223 if (Verbose > 1) {
224 indent (depth);
225 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(g), rootbb.LL.x, rootbb.LL.y, rootbb.UR.x, rootbb.UR.y);
226 }
227
228 LIST_FREE(&vals);
229 LIST_FREE(&gs);
230 LIST_FREE(&children);
231 free (pts);
232}
233
234static void
235reposition (Agraph_t* g, int depth)
236{
237 boxf bb = GD_bb(g);
238
239 if (Verbose > 1) {
240 indent (depth);
241 fprintf (stderr, "reposition %s\n", agnameof(g));
242 }
243
244 /* translate nodes in g but not in a subcluster */
245 if (depth) {
246 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
247 if (PARENT(n) != g)
248 continue;
249 ND_coord(n).x += bb.LL.x;
250 ND_coord(n).y += bb.LL.y;
251 if (Verbose > 1) {
252 indent (depth);
253 fprintf (stderr, "%s : %f %f\n", agnameof(n), ND_coord(n).x, ND_coord(n).y);
254 }
255 }
256 }
257
258 /* translate top-level clusters and recurse */
259 for (int i = 1; i <= GD_n_cluster(g); i++) {
260 Agraph_t *const subg = GD_clust(g)[i];
261 if (depth) {
262 boxf sbb = GD_bb(subg);
263 sbb.LL.x += bb.LL.x;
264 sbb.LL.y += bb.LL.y;
265 sbb.UR.x += bb.LL.x;
266 sbb.UR.y += bb.LL.y;
267 if (Verbose > 1) {
268 indent (depth);
269 fprintf (stderr, "%s : %f %f %f %f\n", agnameof(subg), sbb.LL.x, sbb.LL.y, sbb.UR.x, sbb.UR.y);
270 }
271 GD_bb(subg) = sbb;
272 }
273 reposition (subg, depth+1);
274 }
275
276}
277
278static void
279mkClusters (Agraph_t* g, clist_t* pclist, Agraph_t* parent)
280{
281 clist_t list = {0};
282 clist_t* clist;
283
284 if (pclist == NULL) {
285 // [0] is empty. The clusters are in [1..cnt].
286 LIST_APPEND(&list, NULL);
287 clist = &list;
288 }
289 else
290 clist = pclist;
291
292 for (graph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
293 if (is_a_cluster(subg)) {
294 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
295 do_graph_label (subg);
296 LIST_APPEND(clist, subg);
297 mkClusters(subg, NULL, subg);
298 }
299 else {
300 mkClusters(subg, clist, parent);
301 }
302 }
303 if (pclist == NULL) {
304 assert(LIST_SIZE(&list) - 1 <= INT_MAX);
305 GD_n_cluster(g) = (int)(LIST_SIZE(&list) - 1);
306 if (LIST_SIZE(&list) > 1) {
307 LIST_SHRINK_TO_FIT(&list);
308 LIST_DETACH(&list, &GD_clust(g), NULL);
309 } else {
310 LIST_FREE(&list);
311 }
312 }
313}
314
316{
317 cluster_init_graph(g);
318 mkClusters(g, NULL, g);
319 layout(g, 0);
320 reposition (g, 0);
321
322 if (GD_drawing(g)->ratio_kind) {
323 Agnode_t* n;
324 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
325 ND_pos(n)[0] = PS2INCH(ND_coord(n).x);
326 ND_pos(n)[1] = PS2INCH(ND_coord(n).y);
327 }
328 spline_edges0(g, true);
329 }
330 else {
331 int et = EDGE_TYPE (g);
332 if (et != EDGETYPE_NONE) spline_edges1(g, et);
333 }
335}
336
337static void cleanup_graphs (Agraph_t *g)
338{
339 for (int i = 1; i <= GD_n_cluster(g); i++) {
340 graph_t *const subg = GD_clust(g)[i];
341 free_label(GD_label(subg));
342 cleanup_graphs (subg);
343 }
344 free (GD_clust(g));
345}
346
348{
349 for (node_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
350 for (edge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
352 }
354 }
356}
357
#define DFLT_MARGIN
Definition adjust.h:23
Memory allocation wrappers that exit on failure.
#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:37
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:337
static void indent(int i)
Definition osageinit.c:36
static void mkClusters(Agraph_t *g, clist_t *pclist, Agraph_t *parent)
Definition osageinit.c:279
static void reposition(Agraph_t *g, int depth)
Definition osageinit.c:235
void osage_cleanup(Agraph_t *g)
Definition osageinit.c:347
#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:315
#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