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