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