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