Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
patchwork.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#include <cgraph/alloc.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <patchwork/patchwork.h>
15#include <patchwork/tree_map.h>
16#include <common/render.h>
17
18typedef struct treenode_t treenode_t;
31
32#define DFLT_SZ 1.0
33#define SCALE 1000.0 /* scale up so that 1 is a reasonable default size */
34
35#ifdef DEBUG
36void dumpTree (treenode_t* r, int ind)
37{
38 int i;
39 treenode_t* cp;
40
41 for (i=0; i < ind; i++) fputs(" ", stderr);
42 fprintf (stderr, "%s (%f)\n", (r->kind == AGNODE?agnameof(r->u.n):agnameof(r->u.subg)), r->area);
43 for (cp = r->leftchild; cp; cp = cp->rightsib)
44 dumpTree (cp, ind+1);
45}
46#endif
47
48/* fullArea:
49 * Allow extra area for specified inset. Assume p->kind = AGRAPH
50 * and p->child_area is set. At present, inset is additive; we
51 * may want to allow a multiplicative inset as well.
52 */
53static double fullArea (treenode_t* p, attrsym_t* mp)
54{
55 double m = late_double (p->u.subg, mp, 0, 0);
56 double wid = 2.0 * m + sqrt(p->child_area);
57 return wid * wid;
58}
59
60static double getArea (void* obj, attrsym_t* ap)
61{
62 double area = late_double (obj, ap, DFLT_SZ, 0);
63 if (area == 0) area = DFLT_SZ;
64 area *= SCALE;
65 return area;
66}
67
68/* mkTreeNode:
69 */
71{
72 treenode_t *p = gv_alloc(sizeof(treenode_t));
73
74 p->area = getArea (n, ap);
75 p->kind = AGNODE;
76 p->u.n = n;
77
78 return p;
79}
80
81#define INSERT(cp) if(!first) first=cp; if(prev) prev->rightsib=cp; prev=cp;
82
83/* mkTree:
84 * Recursively build tree from graph
85 * Pre-condition: agnnodes(g) != 0
86 */
88{
89 treenode_t *p = gv_alloc(sizeof(treenode_t));
90 Agraph_t *subg;
91 Agnode_t *n;
92 treenode_t *cp;
93 treenode_t *first = 0;
94 treenode_t *prev = 0;
95 int i;
96 double area = 0;
97
98 p->kind = AGRAPH;
99 p->u.subg = g;
100
101 size_t n_children = 0;
102 for (i = 1; i <= GD_n_cluster(g); i++) {
103 subg = GD_clust(g)[i];
104 cp = mkTree (subg, gp, ap, mp);
105 n_children++;
106 area += cp->area;
107 INSERT(cp);
108 }
109
110 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
111 if (SPARENT(n))
112 continue;
113 cp = mkTreeNode (n, ap);
114 n_children++;
115 area += cp->area;
116 INSERT(cp);
117 SPARENT(n) = g;
118 }
119
120 p->n_children = n_children;
121 if (n_children) {
122 p->child_area = area;
123 p->area = fullArea (p, mp);
124 }
125 else {
126 p->area = getArea (g, gp);
127 }
128 p->leftchild = first;
129
130 return p;
131}
132
133static int nodecmp(const void *x, const void *y) {
134 const treenode_t *const *p0 = x;
135 const treenode_t *const *p1 = y;
136 if ((*p0)->area < (*p1)->area) {
137 return 1;
138 }
139 if ((*p0)->area > (*p1)->area) {
140 return -1;
141 }
142 return 0;
143}
144
146{
147 rectangle *recs;
148 treenode_t* cp;
149
150 /* if (tree->kind == AGNODE) return; */
151 if (tree->n_children == 0) return;
152
153 size_t nc = tree->n_children;
154 treenode_t** nodes = gv_calloc(nc, sizeof(treenode_t*));
155 cp = tree->leftchild;
156 for (size_t i = 0; i < nc; i++) {
157 nodes[i] = cp;
158 cp = cp->rightsib;
159 }
160
161 qsort(nodes, nc, sizeof(treenode_t *), nodecmp);
162 double* areas_sorted = gv_calloc(nc, sizeof(double));
163 for (size_t i = 0; i < nc; i++) {
164 areas_sorted[i] = nodes[i]->area;
165 }
166 if (tree->area == tree->child_area)
167 recs = tree_map(nc, areas_sorted, tree->r);
168 else {
169 rectangle crec;
170 double disc, delta, m, h = tree->r.size[1], w = tree->r.size[0];
171 crec.x[0] = tree->r.x[0];
172 crec.x[1] = tree->r.x[1];
173 delta = h - w;
174 disc = sqrt(delta*delta + 4.0*tree->child_area);
175 m = (h + w - disc)/2.0;
176 crec.size[0] = w - m;
177 crec.size[1] = h - m;
178 recs = tree_map(nc, areas_sorted, crec);
179 }
180 if (Verbose)
181 fprintf (stderr, "rec %f %f %f %f\n", tree->r.x[0], tree->r.x[1], tree->r.size[0], tree->r.size[1]);
182 for (size_t i = 0; i < nc; i++) {
183 nodes[i]->r = recs[i];
184 if (Verbose)
185 fprintf (stderr, "%f - %f %f %f %f = %f (%f %f %f %f)\n", areas_sorted[i],
186 recs[i].x[0]-recs[i].size[0]*0.5, recs[i].x[1]-recs[i].size[1]*0.5,
187 recs[i].x[0]+recs[i].size[0]*0.5, recs[i].x[1]+recs[i].size[1]*0.5, recs[i].size[0]*recs[i].size[1],
188 recs[i].x[0], recs[i].x[1], recs[i].size[0], recs[i].size[1]);
189
190 }
191 free (nodes);
192 free (areas_sorted);
193 free (recs);
194
195 cp = tree->leftchild;
196 for (size_t i = 0; i < nc; i++) {
197 if (cp->kind == AGRAPH)
198 layoutTree (cp);
199 cp = cp->rightsib;
200 }
201}
202
203static void finishNode(node_t * n)
204{
205 char buf [40];
206 if (N_fontsize) {
207 char* str = agxget(n, N_fontsize);
208 if (*str == '\0') {
209 snprintf(buf, sizeof(buf), "%.03f", ND_ht(n)*0.7);
210 agxset(n, N_fontsize, buf);
211 }
212 }
214}
215
217{
218 treenode_t *p;
219 Agnode_t *n;
221 rectangle rr;
222 boxf r;
223 double x0, y0, wd, ht;
224
225 if (tree->kind == AGRAPH) {
226 for (p = tree->leftchild; p; p = p->rightsib)
227 walkTree (p);
228 x0 = tree->r.x[0];
229 y0 = tree->r.x[1];
230 wd = tree->r.size[0];
231 ht = tree->r.size[1];
232 r.LL.x = x0 - wd/2.0;
233 r.LL.y = y0 - ht/2.0;
234 r.UR.x = r.LL.x + wd;
235 r.UR.y = r.LL.y + ht;
236 GD_bb(tree->u.subg) = r;
237 }
238 else {
239 rr = tree->r;
240 center.x = rr.x[0];
241 center.y = rr.x[1];
242
243 n = tree->u.n;
244 ND_coord(n) = center;
245 ND_width(n) = PS2INCH(rr.size[0]);
246 ND_height(n) = PS2INCH(rr.size[1]);
248 finishNode(n);
249 if (Verbose)
250 fprintf(stderr,"%s coord %.5g %.5g ht %f width %f\n",
251 agnameof(n), ND_coord(n).x, ND_coord(n).y, ND_ht(n), ND_xsize(n));
252 }
253}
254
255/* freeTree:
256 */
257static void freeTree (treenode_t* tp)
258{
259 treenode_t* cp = tp->leftchild;
260 treenode_t* rp;
261 size_t nc = tp->n_children;
262
263 for (size_t i = 0; i < nc; i++) {
264 rp = cp->rightsib;
265 freeTree (cp);
266 cp = rp;
267 }
268 free (tp);
269}
270
271/* patchworkLayout:
272 */
274{
275 treenode_t* root;
276 attrsym_t * ap = agfindnodeattr(g, "area");
277 attrsym_t * gp = agfindgraphattr(g, "area");
278 attrsym_t * mp = agfindgraphattr(g, "inset");
279 double total;
280
281 root = mkTree (g,gp,ap,mp);
282 total = root->area;
283 root->r = (rectangle){{0, 0}, {sqrt(total + 0.1), sqrt(total + 0.1)}};
284 layoutTree(root);
285 walkTree(root);
286 freeTree (root);
287}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
void common_init_node(node_t *n)
Definition utils.c:422
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:48
void gv_nodesize(node_t *n, bool flip)
Definition utils.c:1556
#define PS2INCH(a_points)
Definition geom.h:70
Agsym_t * N_fontsize
Definition globals.h:84
static int Verbose
Definition gml2gv.c:22
void free(void *)
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:481
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
#define agfindgraphattr(g, a)
Definition types.h:613
#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_flip(g)
Definition types.h:378
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define agfindnodeattr(g, a)
Definition types.h:615
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_coord(n)
Definition types.h:490
#define ND_xsize(n)
Definition types.h:537
Agraph_t * agraphof(void *obj)
Definition obj.c:184
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
@ tree
Definition gvgen.c:33
agxbuf * str
Definition htmlparse.c:97
$2 u p prev
Definition htmlparse.y:495
#define delta
Definition maze.c:133
#define SCALE
Definition patchwork.c:33
static int nodecmp(const void *x, const void *y)
Definition patchwork.c:133
static double fullArea(treenode_t *p, attrsym_t *mp)
Definition patchwork.c:53
static double getArea(void *obj, attrsym_t *ap)
Definition patchwork.c:60
static void finishNode(node_t *n)
Definition patchwork.c:203
static treenode_t * mkTreeNode(Agnode_t *n, attrsym_t *ap)
Definition patchwork.c:70
void patchworkLayout(Agraph_t *g)
Definition patchwork.c:273
static void walkTree(treenode_t *tree)
Definition patchwork.c:216
static void freeTree(treenode_t *tp)
Definition patchwork.c:257
static void layoutTree(treenode_t *tree)
Definition patchwork.c:145
static treenode_t * mkTree(Agraph_t *g, attrsym_t *gp, attrsym_t *ap, attrsym_t *mp)
Definition patchwork.c:87
#define INSERT(cp)
Definition patchwork.c:81
#define DFLT_SZ
Definition patchwork.c:32
#define SPARENT(n)
Definition patchwork.h:25
graph or subgraph
Definition cgraph.h:425
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 y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
double x[2]
Definition tree_map.h:17
double size[2]
Definition tree_map.h:18
treenode_t * leftchild
Definition patchwork.c:23
size_t n_children
Definition patchwork.c:29
rectangle r
Definition patchwork.c:22
Agraph_t * subg
Definition patchwork.c:25
union treenode_t::@104 u
treenode_t * rightsib
Definition patchwork.c:23
double child_area
Definition patchwork.c:21
double area
Definition patchwork.c:20
Agnode_t * n
Definition patchwork.c:26
static point center(point vertex[], size_t n)
rectangle * tree_map(size_t n, double *area, rectangle fillrec)
Definition tree_map.c:102