| 1 | /* $Id$Revision: */ | 
|---|
| 2 | /* vim:set shiftwidth=4 ts=8: */ | 
|---|
| 3 |  | 
|---|
| 4 | /************************************************************************* | 
|---|
| 5 | * Copyright (c) 2011 AT&T Intellectual Property | 
|---|
| 6 | * All rights reserved. This program and the accompanying materials | 
|---|
| 7 | * are made available under the terms of the Eclipse Public License v1.0 | 
|---|
| 8 | * which accompanies this distribution, and is available at | 
|---|
| 9 | * http://www.eclipse.org/legal/epl-v10.html | 
|---|
| 10 | * | 
|---|
| 11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ | 
|---|
| 12 | *************************************************************************/ | 
|---|
| 13 |  | 
|---|
| 14 | #include <stdio.h> | 
|---|
| 15 | #include <stdlib.h> | 
|---|
| 16 | #include <patchwork.h> | 
|---|
| 17 | #include <tree_map.h> | 
|---|
| 18 | #include "render.h" | 
|---|
| 19 |  | 
|---|
| 20 | typedef struct treenode_t treenode_t; | 
|---|
| 21 | struct treenode_t { | 
|---|
| 22 | double area; | 
|---|
| 23 | double child_area; | 
|---|
| 24 | rectangle r; | 
|---|
| 25 | treenode_t *leftchild, *rightsib; | 
|---|
| 26 | union { | 
|---|
| 27 | Agraph_t *subg; | 
|---|
| 28 | Agnode_t *n; | 
|---|
| 29 | } u; | 
|---|
| 30 | int kind; | 
|---|
| 31 | int n_children; | 
|---|
| 32 | }; | 
|---|
| 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 | 
|---|
| 38 | void 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 | */ | 
|---|
| 55 | static double fullArea (treenode_t* p, attrsym_t* mp) | 
|---|
| 56 | { | 
|---|
| 57 | double m = late_double (p->u.subg, mp, 0, 0); | 
|---|
| 58 | if (m == 0) return p->child_area; | 
|---|
| 59 | else { | 
|---|
| 60 | double wid = (2.0*m + sqrt(p->child_area)); | 
|---|
| 61 | return wid*wid; | 
|---|
| 62 | } | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | static double getArea (void* obj, attrsym_t* ap) | 
|---|
| 66 | { | 
|---|
| 67 | double area = late_double (obj, ap, DFLT_SZ, 0); | 
|---|
| 68 | if (area == 0) area = DFLT_SZ; | 
|---|
| 69 | area *= SCALE; | 
|---|
| 70 | return area; | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | /* mkTreeNode: | 
|---|
| 74 | */ | 
|---|
| 75 | static treenode_t* mkTreeNode (Agnode_t* n, attrsym_t* ap) | 
|---|
| 76 | { | 
|---|
| 77 | treenode_t *p = NEW(treenode_t); | 
|---|
| 78 |  | 
|---|
| 79 | p->area = getArea (n, ap); | 
|---|
| 80 | p->kind = AGNODE; | 
|---|
| 81 | p->u.n = n; | 
|---|
| 82 |  | 
|---|
| 83 | return p; | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | #define INSERT(cp) if(!first) first=cp; if(prev) prev->rightsib=cp; prev=cp; | 
|---|
| 87 |  | 
|---|
| 88 | /* mkTree: | 
|---|
| 89 | * Recursively build tree from graph | 
|---|
| 90 | * Pre-condition: agnnodes(g) != 0 | 
|---|
| 91 | */ | 
|---|
| 92 | static treenode_t *mkTree (Agraph_t * g, attrsym_t* gp, attrsym_t* ap, attrsym_t* mp) | 
|---|
| 93 | { | 
|---|
| 94 | treenode_t *p = NEW(treenode_t); | 
|---|
| 95 | Agraph_t *subg; | 
|---|
| 96 | Agnode_t *n; | 
|---|
| 97 | treenode_t *cp; | 
|---|
| 98 | treenode_t *first = 0; | 
|---|
| 99 | treenode_t *prev = 0; | 
|---|
| 100 | int i, n_children = 0; | 
|---|
| 101 | double area = 0; | 
|---|
| 102 |  | 
|---|
| 103 | p->kind = AGRAPH; | 
|---|
| 104 | p->u.subg = g; | 
|---|
| 105 |  | 
|---|
| 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 |  | 
|---|
| 137 | static int nodecmp (treenode_t** p0, treenode_t** p1) | 
|---|
| 138 | { | 
|---|
| 139 | double diff = (*p0)->area - (*p1)->area; | 
|---|
| 140 |  | 
|---|
| 141 | if (diff < 0) return 1; | 
|---|
| 142 | else if (diff > 0) return -1; | 
|---|
| 143 | else return 0; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | static void layoutTree(treenode_t * tree) | 
|---|
| 147 | { | 
|---|
| 148 | rectangle *recs; | 
|---|
| 149 | treenode_t** nodes; | 
|---|
| 150 | double* areas_sorted; | 
|---|
| 151 | int i, nc; | 
|---|
| 152 | treenode_t* cp; | 
|---|
| 153 |  | 
|---|
| 154 | /* if (tree->kind == AGNODE) return; */ | 
|---|
| 155 | if (tree->n_children == 0) return; | 
|---|
| 156 |  | 
|---|
| 157 | nc = tree->n_children; | 
|---|
| 158 | nodes = N_NEW(nc, treenode_t*); | 
|---|
| 159 | cp = tree->leftchild; | 
|---|
| 160 | for (i = 0; i < nc; i++) { | 
|---|
| 161 | nodes[i] = cp; | 
|---|
| 162 | cp = cp->rightsib; | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | qsort (nodes, nc, sizeof(treenode_t*), (qsort_cmpf)nodecmp); | 
|---|
| 166 | areas_sorted = N_NEW(nc,double); | 
|---|
| 167 | for (i = 0; i < nc; i++) { | 
|---|
| 168 | areas_sorted[i] = nodes[i]->area; | 
|---|
| 169 | } | 
|---|
| 170 | if (tree->area == tree->child_area) | 
|---|
| 171 | recs = tree_map(nc, areas_sorted, tree->r); | 
|---|
| 172 | else { | 
|---|
| 173 | rectangle crec; | 
|---|
| 174 | double disc, delta, m, h = tree->r.size[1], w = tree->r.size[0]; | 
|---|
| 175 | crec.x[0] = tree->r.x[0]; | 
|---|
| 176 | crec.x[1] = tree->r.x[1]; | 
|---|
| 177 | delta = h - w; | 
|---|
| 178 | disc = sqrt(delta*delta + 4.0*tree->child_area); | 
|---|
| 179 | m = (h + w - disc)/2.0; | 
|---|
| 180 | crec.size[0] = w - m; | 
|---|
| 181 | crec.size[1] = h - m; | 
|---|
| 182 | recs = tree_map(nc, areas_sorted, crec); | 
|---|
| 183 | } | 
|---|
| 184 | if (Verbose) | 
|---|
| 185 | fprintf (stderr, "rec %f %f %f %f\n", tree->r.x[0], tree->r.x[1], tree->r.size[0], tree->r.size[1]); | 
|---|
| 186 | for (i = 0; i < nc; i++) { | 
|---|
| 187 | nodes[i]->r = recs[i]; | 
|---|
| 188 | if (Verbose) | 
|---|
| 189 | fprintf (stderr, "%f - %f %f %f %f = %f (%f %f %f %f)\n", areas_sorted[i], | 
|---|
| 190 | recs[i].x[0]-recs[i].size[0]*0.5, recs[i].x[1]-recs[i].size[1]*0.5, | 
|---|
| 191 | 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], | 
|---|
| 192 | recs[i].x[0], recs[i].x[1],  recs[i].size[0], recs[i].size[1]); | 
|---|
| 193 |  | 
|---|
| 194 | } | 
|---|
| 195 | free (nodes); | 
|---|
| 196 | free (areas_sorted); | 
|---|
| 197 | free (recs); | 
|---|
| 198 |  | 
|---|
| 199 | cp = tree->leftchild; | 
|---|
| 200 | for (i = 0; i < nc; i++) { | 
|---|
| 201 | if (cp->kind == AGRAPH) | 
|---|
| 202 | layoutTree (cp); | 
|---|
| 203 | cp = cp->rightsib; | 
|---|
| 204 | } | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | static void finishNode(node_t * n) | 
|---|
| 208 | { | 
|---|
| 209 | char buf [40]; | 
|---|
| 210 | if (N_fontsize) { | 
|---|
| 211 | char* str = agxget(n, N_fontsize); | 
|---|
| 212 | if (*str == '\0') { | 
|---|
| 213 | sprintf (buf, "%.03f", ND_ht(n)*0.7); | 
|---|
| 214 | agxset(n, N_fontsize, buf); | 
|---|
| 215 | } | 
|---|
| 216 | } | 
|---|
| 217 | common_init_node (n); | 
|---|
| 218 | } | 
|---|
| 219 |  | 
|---|
| 220 | static void walkTree(treenode_t * tree) | 
|---|
| 221 | { | 
|---|
| 222 | treenode_t *p; | 
|---|
| 223 | Agnode_t *n; | 
|---|
| 224 | pointf center; | 
|---|
| 225 | rectangle rr; | 
|---|
| 226 | boxf r; | 
|---|
| 227 | double x0,  y0, wd, ht; | 
|---|
| 228 |  | 
|---|
| 229 | if (tree->kind == AGRAPH) { | 
|---|
| 230 | for (p = tree->leftchild; p; p = p->rightsib) | 
|---|
| 231 | walkTree (p); | 
|---|
| 232 | x0 = tree->r.x[0]; | 
|---|
| 233 | y0 = tree->r.x[1]; | 
|---|
| 234 | wd = tree->r.size[0]; | 
|---|
| 235 | ht = tree->r.size[1]; | 
|---|
| 236 | r.LL.x = x0 - wd/2.0; | 
|---|
| 237 | r.LL.y = y0 - ht/2.0; | 
|---|
| 238 | r.UR.x = r.LL.x + wd; | 
|---|
| 239 | r.UR.y = r.LL.y + ht; | 
|---|
| 240 | GD_bb(tree->u.subg) = r; | 
|---|
| 241 | } | 
|---|
| 242 | else { | 
|---|
| 243 | rr = tree->r; | 
|---|
| 244 | center.x = rr.x[0]; | 
|---|
| 245 | center.y = rr.x[1]; | 
|---|
| 246 |  | 
|---|
| 247 | n = tree->u.n; | 
|---|
| 248 | ND_coord(n) = center; | 
|---|
| 249 | ND_width(n) = PS2INCH(rr.size[0]); | 
|---|
| 250 | ND_height(n) = PS2INCH(rr.size[1]); | 
|---|
| 251 | gv_nodesize(n, GD_flip(agraphof(n))); | 
|---|
| 252 | finishNode(n); | 
|---|
| 253 | if (Verbose) | 
|---|
| 254 | fprintf(stderr, "%s coord %.5g %.5g ht %f width %f\n", | 
|---|
| 255 | agnameof(n), ND_coord(n).x, ND_coord(n).y, ND_ht(n), ND_xsize(n)); | 
|---|
| 256 | } | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | /* freeTree: | 
|---|
| 260 | */ | 
|---|
| 261 | static void freeTree (treenode_t* tp) | 
|---|
| 262 | { | 
|---|
| 263 | treenode_t* cp = tp->leftchild; | 
|---|
| 264 | treenode_t* rp; | 
|---|
| 265 | int i, nc = tp->n_children; | 
|---|
| 266 |  | 
|---|
| 267 | for (i = 0; i < nc; i++) { | 
|---|
| 268 | rp = cp->rightsib; | 
|---|
| 269 | freeTree (cp); | 
|---|
| 270 | cp = rp; | 
|---|
| 271 | } | 
|---|
| 272 | free (tp); | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | /* patchworkLayout: | 
|---|
| 276 | */ | 
|---|
| 277 | void patchworkLayout(Agraph_t * g) | 
|---|
| 278 | { | 
|---|
| 279 | treenode_t* root; | 
|---|
| 280 | attrsym_t * ap = agfindnodeattr(g, "area"); | 
|---|
| 281 | attrsym_t * gp = agfindgraphattr(g, "area"); | 
|---|
| 282 | attrsym_t * mp = agfindgraphattr(g, "inset"); | 
|---|
| 283 | double total; | 
|---|
| 284 |  | 
|---|
| 285 | root = mkTree (g,gp,ap,mp); | 
|---|
| 286 | total = root->area; | 
|---|
| 287 | root->r = rectangle_new(0, 0, sqrt(total + 0.1), sqrt(total + 0.1)); | 
|---|
| 288 | layoutTree(root); | 
|---|
| 289 | walkTree(root); | 
|---|
| 290 | freeTree (root); | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|