]> granicus.if.org Git - graphviz/commitdiff
Update patchwork code incorporating Yifan's treemap function.
authorerg <devnull@localhost>
Tue, 15 Feb 2011 17:45:08 +0000 (17:45 +0000)
committererg <devnull@localhost>
Tue, 15 Feb 2011 17:45:08 +0000 (17:45 +0000)
lib/patchwork/Makefile.am
lib/patchwork/patchwork.c
lib/patchwork/patchwork.h
lib/patchwork/patchworkinit.c
lib/patchwork/tree_map.c [new file with mode: 0644]
lib/patchwork/tree_map.h [new file with mode: 0644]

index 3010b76f9bb67f1a9045bf4ebd2f3671dd897268..b936eda673743c814a5e2ae2647187dd6cd9db4e 100644 (file)
@@ -19,9 +19,9 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib/$(GRAPH) \
        -I$(top_srcdir)/lib/cdt
 
-noinst_HEADERS = patchwork.h
+noinst_HEADERS = patchwork.h tree_map.h
 noinst_LTLIBRARIES = libpatchwork_C.la
 
-libpatchwork_C_la_SOURCES = patchwork.c patchworkinit.c
+libpatchwork_C_la_SOURCES = patchwork.c patchworkinit.c tree_map.c
 
 EXTRA_DIST = Makefile.old gvpatchwork.vcproj
index 4cded4b0992f23d1d8da8fa9549c451e9a04e0ad..ebcd0572b531b02b847c19ac4c6ce085fcf9c1b0 100644 (file)
+/* $Id$Revision: */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <patchwork.h>
+#include <tree_map.h>
 #include "render.h"
 
-extern void patchwork_init_graph(graph_t * g);
-
-#ifndef HAVE_DRAND48
-extern double drand48(void);
-#endif
-
-typedef boxf rect_t;
-
-typedef struct treenode_t {
-       double area;
-       rect_t  r;
-       struct treenode_t *leftchild, *rightsib;
-       union {Agraph_t *subg; Agnode_t *n;} u;
-       int             kind;
-} treenode_t;
-
-#define BT 101
-#define LR 202
-
-typedef struct rlist_s {
-       treenode_t *elt;
-       int n, extent;
-       double sum;
-} rlist_t;
-
-static treenode_t *newtreenode(treenode_t **first, treenode_t **prev)
-{
-       treenode_t *p;
-       p = NEW(treenode_t);
-       if (!*first) *first = p;
-       if (*prev) (*prev)->rightsib = p;
-       *prev = p;
-       return p;
-}
-
-/* return list of treenodes */
-static treenode_t *treebuilder(Agraph_t *g)
+typedef struct treenode_t treenode_t;
+struct treenode_t {
+    double area;
+    rectangle r;
+    treenode_t *leftchild, *rightsib;
+    union {
+       Agraph_t *subg;
+       Agnode_t *n;
+    } u;
+    int kind;
+    int n_children;
+};
+
+/* mkTreeNode:
+ */
+static treenode_t* mkTreeNode (Agnode_t* n, attrsym_t* ap)
 {
-       int                     i;
-       treenode_t      *first = 0;
-       treenode_t      *prev = 0;
-       treenode_t      *p;
-       Agraph_t        *subg;
-       Agnode_t        *n;
+    treenode_t *p = NEW(treenode_t);
 
-       for (i = 1; i <= GD_n_cluster(g); i++) {
-               subg = GD_clust(g)[i];
-               if (agnnodes(subg) == 0) continue;
-               p = newtreenode(&first,&prev);
-#ifndef WITH_CGRAPH
-               p->kind = AGGRAPH;
-#else
-               p->kind = AGRAPH;
-#endif
-               p->u.subg = subg;
-               p->leftchild = treebuilder(subg);
-       }
+    p->area = late_double (n, ap, 1.0, 0);
+    if (p->area == 0) p->area = 1.0;
+    p->kind = AGNODE;
+    p->u.n = n;
 
-       for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
-               char *val;
-               if (SPARENT(n)) continue;
-               p = newtreenode(&first,&prev);
-               val = agget(n,"area");
-               if (val) p->area = atof(val);
-               if (p->area <= 0.0) p->area = 1.0;
-               p->kind = AGNODE;
-               p->u.n = n;
-               SPARENT(n) = g;
-       }
-       return first;
+    return p;
 }
 
-/* get recursive area requirements */
-static double sizeit(treenode_t *tree)
+#define INSERT(cp) if(!first) first=cp; if(prev) prev->rightsib=cp; prev=cp;
+
+/* mkTree:
+ * Recursively build tree from graph
+ * Pre-condition: agnnodes(g) != 0
+ */
+static treenode_t *mkTree (Agraph_t * g,  attrsym_t* ap)
 {
-       treenode_t      *p;
-       double          mysize = 0.0;
+    treenode_t *p = NEW(treenode_t);
+    Agraph_t *subg;
+    Agnode_t *n;
+    treenode_t *cp;
+    treenode_t *first = 0;
+    treenode_t *prev = 0;
+    int i, n_children = 0;
+    double area = 0;
+
+    p->kind = AGRAPH;
+    p->u.subg = g;
+
+    for (i = 1; i <= GD_n_cluster(g); i++) {
+       subg = GD_clust(g)[i];
+       if (agnnodes(subg) == 0)
+           continue;
+       cp = mkTree (subg, ap);
+       n_children++;
+       area += cp->area;
+       INSERT(cp);
+    }
 
-       if (tree->leftchild) {
-               for (p = tree->leftchild; p; p = p->rightsib) mysize += sizeit(p);
-               tree->area = mysize;
-       }
-       return tree->area;
-}
+    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+       if (SPARENT(n))
+           continue;
+       cp = mkTreeNode (n, ap);
+       n_children++;
+       area += cp->area;
+       INSERT(cp);
+       SPARENT(n) = g;
+    }
 
-static rect_t mkrectangle(double x0, double y0, double x1, double y1)
-{
-       rect_t  rv;
-       rv.LL.x = x0; rv.LL.y = y0; rv.UR.x = x1; rv.UR.y = y1;
-       return rv;
-}
+    p->n_children = n_children;
+    p->area = area;
+    p->leftchild = first;
 
-static double weight(treenode_t *tree, treenode_t *split)
-{
-       double rv = 0.0;
-       while (tree && (tree!=split)) {rv = rv + tree->area; tree = tree->rightsib;}
-       return rv;
+    return p;
 }
 
-static void layout(treenode_t *tree, treenode_t *split, rect_t r)
+static int nodecmp (treenode_t** p0, treenode_t** p1)
 {
-       double size, halfsize, w1, tmp, width, height;
-       rect_t r1,r2;
-       treenode_t *p;
-
-       if (!tree) return;
-       if (!tree->rightsib || (tree->rightsib==split)) {tree->r = r; layout(tree->leftchild,0,r); return;}
-       size = weight(tree,split);
-       halfsize = size / 2.0;
-       w1 = 0; tmp = 0;
-
-       for (p = tree; p && (p != split); p = p->rightsib) {
-               tmp = w1 + p->area;
-               if (abs(halfsize - tmp) > abs(halfsize- w1))
-                       break;
-               w1 = tmp;
-       }
+    double diff = (*p0)->area - (*p1)->area;
 
-       width = r.UR.x - r.LL.x;
-       height = r.UR.y - r.LL.y;
-       if (width > height) {
-               r1 = mkrectangle(r.LL.x,r.LL.y,r.LL.x + width * w1 / size, r.UR.y);
-               r2 = mkrectangle(r1.UR.x,r.LL.y,r.UR.x,r.UR.y);
-       }
-       else {
-       /*  this was bottom to top - but we want top to bottom layout 
-               r1 = mkrectangle(r.LL.x,r.LL.y,r.UR.x,r.LL.y + height * w1 / size);
-               r2 = mkrectangle(r.LL.x,r1.UR.y,r.UR.x,r.UR.y);
-       */
-               r1 = mkrectangle(r.LL.x,r.UR.y - height * w1 / size,r.UR.x,r.UR.y);
-               r2 = mkrectangle(r.LL.x,r.LL.y,r.UR.x,r1.LL.y);
-       }
-       layout(tree,p,r1);
-       layout(p,split,r2);
+    if (diff < 0) return 1;
+    else if (diff > 0) return -1;
+    else return 0;
 }
 
-static void printer(treenode_t *tree)
+static void layoutTree(treenode_t * tree)
 {
-       static int onetime = 1;
-       treenode_t      *p;
+    rectangle *recs;
+    treenode_t** nodes;
+    double* areas_sorted;
+    int i, nc;
+    treenode_t* cp;
+
+    if (tree->kind == AGNODE) return;
+
+    nc = tree->n_children;
+    nodes = N_NEW(nc, treenode_t*);
+    cp = tree->leftchild;
+    for (i = 0; i < nc; i++) {
+       nodes[i] = cp;
+       cp = cp->rightsib;
+    }
 
-       if (onetime) { fprintf(stderr,"%%!PS\n"); onetime=0;}
-       fprintf(stderr,"newpath %.3lf %.3lf moveto %.3lf %.3lf lineto %.3lf %.3lf lineto %.3lf %.3lf lineto closepath \n",
-               tree->r.LL.x,tree->r.LL.y,
-               tree->r.UR.x,tree->r.LL.y,
-               tree->r.UR.x,tree->r.UR.y,
-               tree->r.LL.x,tree->r.UR.y);
-       if (tree->leftchild) {
-               fprintf(stderr,"stroke \n");
-               for (p = tree->leftchild; p; p = p->rightsib) printer(p);
-       }
-       else {
-               fprintf(stderr,"gsave %.3lf 1.0 1.0 sethsbcolor fill grestore stroke\n",drand48());
-       }
-}
+    qsort (nodes, nc, sizeof(treenode_t*), (qsort_cmpf)nodecmp);
+    areas_sorted = N_NEW(nc,double);
+    for (i = 0; i < nc; i++) {
+       areas_sorted[i] = nodes[i]->area;
+    }
+    recs = tree_map(nc, areas_sorted, tree->r);
+    if (Verbose)
+       fprintf (stderr, "rec %f %f %f %f\n", tree->r.x[0], tree->r.x[1], tree->r.size[0], tree->r.size[1]);
+    for (i = 0; i < nc; i++) {
+       nodes[i]->r = recs[i];
+       if (Verbose)
+           fprintf (stderr, "%f - %f %f %f %f = %f (%f %f %f %f)\n", areas_sorted[i],
+               recs[i].x[0]-recs[i].size[0]*0.5, recs[i].x[1]-recs[i].size[1]*0.5,
+               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],
+               recs[i].x[0], recs[i].x[1],  recs[i].size[0], recs[i].size[1]);
 
-static void finishNode (node_t* n)
-{
-    char* str = strdup_and_subst_obj(NODENAME_ESC, (void*)n);
-    ND_shape(n) = bind_shape("box", n);
-    ND_label(n) = make_label((void*)n, str, LT_NONE,
-               late_double(n, N_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE),
-               late_nnstring(n, N_fontname, DEFAULT_FONTNAME),
-               late_nnstring(n, N_fontcolor, DEFAULT_COLOR));
-    ND_shape(n)->fns->initfn(n);
+    }
+    free (nodes);
+    free (areas_sorted);
+    free (recs);
+
+    cp = tree->leftchild;
+    for (i = 0; i < nc; i++) {
+       if (cp->kind == AGRAPH)
+           layoutTree (cp);
+       cp = cp->rightsib;
+    }
 }
 
-static rect_t walker(treenode_t *tree)
+static void finishNode(node_t * n)
 {
-    treenode_t *p;
-    Agnode_t   *n;
-    pointf             center;
-    rect_t      r, rr;
-
-    switch(tree->kind) {
+    char buf [40];
+    if (N_fontsize) {
 #ifndef WITH_CGRAPH
-       case AGGRAPH:
+       char* str = agxget(n, N_fontsize->index);
 #else
-       case AGRAPH:
+       char* str = agxget(n, N_fontsize);
 #endif
-           break;
-       case AGNODE:
-           rr = tree->r;
-           center.x = (tree->r.UR.x + tree->r.LL.x) / 2.0;
-           center.y = (tree->r.UR.y + tree->r.LL.y) / 2.0;
+       if (*str == '\0') {
+           sprintf (buf, "%.03f", ND_ht(n)*0.7); 
+#ifndef WITH_CGRAPH
+           agxset(n, N_fontsize->index, buf);
+#else /* WITH_CGRAPH */
+           agxset(n, N_fontsize, buf);
+#endif /* WITH_CGRAPH */
+       }
+    }
+    common_init_node (n);
+}
 
-           n = tree->u.n;
-           ND_coord(n) = center;
-           ND_height(n) = PS2INCH(tree->r.UR.y - tree->r.LL.y);
-           ND_width(n) = PS2INCH(tree->r.UR.x - tree->r.LL.x);
-           gv_nodesize(n,GD_flip(agraphof(n)));
-           finishNode (n);
-           /*fprintf(stderr,"%s coord %.5g %.5g ht %d width %d\n",
-                   agnameof(n), ND_coord(n).x, ND_coord(n).y, ND_ht(n),
-                   ND_rw(n)+ND_lw(n));*/
-           break;
-       default: abort();
+static void walkTree(treenode_t * tree)
+{
+    treenode_t *p;
+    Agnode_t *n;
+    pointf center;
+    rectangle rr;
+    boxf r;
+    double x0,  y0, wd, ht;
+
+    if (tree->kind == AGRAPH) {
+       for (p = tree->leftchild; p; p = p->rightsib)
+           walkTree (p);
+       x0 = tree->r.x[0];
+       y0 = tree->r.x[1];
+       wd = tree->r.size[0];
+       ht = tree->r.size[1];
+       r.LL.x = x0 - wd/2.0;
+       r.LL.y = y0 - ht/2.0;
+       r.UR.x = r.LL.x + wd;
+       r.UR.y = r.LL.y + ht;
+       GD_bb(tree->u.subg) = r;
     }
-    if ((p = tree->leftchild)) {
-       rr = walker (p);
-       p = p->rightsib;
-       for (; p; p = p->rightsib) {
-           r = walker(p);
-           EXPANDBB(rr,r);
-       }
-    rr.LL.x -= 2./72.;
-    rr.LL.y -= 2./72.;
-    rr.UR.x += 2./72.;
-    rr.UR.y += 2./72.;
-       GD_bb(tree->u.subg) = rr;
+    else {
+       rr = tree->r;
+       center.x = rr.x[0];
+       center.y = rr.x[1];
+
+       n = tree->u.n;
+       ND_coord(n) = center;
+       ND_width(n) = PS2INCH(rr.size[0]);
+       ND_height(n) = PS2INCH(rr.size[1]);
+       gv_nodesize(n, GD_flip(agraphof(n)));
+       finishNode(n);
+       if (Verbose)
+           fprintf(stderr,"%s coord %.5g %.5g ht %f width %f\n",
+               agnameof(n), ND_coord(n).x, ND_coord(n).y, ND_ht(n), ND_xsize(n));
     }
-    return rr;
 }
 
-#ifdef PWDRIVER
-int main()
+/* freeTree:
+ */
+static void freeTree (treenode_t* tp)
 {
-       static treenode_t       root;
-       rect_t r = {{0.0, 0.0}, {100.0, 100.0}};
-       Agraph_t *g;
+    treenode_t* cp = tp->leftchild;
+    int i, nc = tp->n_children;
 
-       aginit();
-       g = agread(stdin);
-#ifndef WITH_CGRAPH
-       root.kind = AGGRAPH;
-#else
-       root.kind = AGRAPH;
-#endif
-       root.leftchild = treebuilder(g);
-       sizeit(&root);
-       /*layouter(&root,LR,r);*/
-       printer(&root);
-       return 0;
+    for (i = 0; i < nc; i++) {
+       freeTree (cp);
+       cp = cp->rightsib;
+    }
+    free (tp);
 }
-#endif
 
-void patchwork_layout(Agraph_t *g)
+/* patchworkLayout:
+ */
+void patchworkLayout(Agraph_t * g)
 {
-       static treenode_t       root;
-       rect_t r = {{0.0, 0.0}, {100.0, 100.0}};
-       patchwork_init_graph(g);
-#ifndef WITH_CGRAPH
-       root.kind = AGGRAPH;
-#else
-       root.kind = AGRAPH;
-#endif
-       root.leftchild = treebuilder(g);
-       root.u.subg = g;
-       sizeit(&root);
-       layout(&root,0,r);
-       /* printer(&root); */
-       walker(&root);
-       /* compute_bb(g); */
-       /* fprintf(stderr,"bb %d %d %d %d\n", */
-               /* GD_bb(g).LL.x, GD_bb(g).LL.y, GD_bb(g).UR.x, GD_bb(g).UR.y); */
-       dotneato_postprocess(g);
+    treenode_t* root;
+    attrsym_t * ap = agfindnodeattr(g, "size");
+    double total;
+
+    root = mkTree (g,ap);
+    total = root->area;
+    root->r = rectangle_new(0, 0, sqrt(total + 0.1), sqrt(total + 0.1));
+    layoutTree(root);
+    walkTree(root);
+    freeTree (root);
 }
index 6a66e76ba335c7b0d6aa7783c05b91b77e19b3d1..ee3df7a9712e622554a9fcf9cd5e3f905b7363ff 100644 (file)
@@ -1,3 +1,4 @@
+/* $Id$Revision: */
 /* vim:set shiftwidth=4 ts=8: */
 
 /*************************************************************************
@@ -21,27 +22,15 @@ extern "C" {
 #endif
 
     typedef struct {
-       int nStepsToLeaf;
-       int subtreeSize;
-       int nChildren;
-       int nStepsToCenter;
        graph_t *parent;
-       double span;
-       double theta;
     } rdata;
 
 #define RDATA(n) ((rdata*)(ND_alg(n)))
-#define SLEAF(n) (RDATA(n)->nStepsToLeaf)
-#define STSIZE(n) (RDATA(n)->subtreeSize)
-#define NCHILD(n) (RDATA(n)->nChildren)
-#define SCENTER(n) (RDATA(n)->nStepsToCenter)
 #define SPARENT(n) (RDATA(n)->parent)
-#define SPAN(n) (RDATA(n)->span)
-#define THETA(n) (RDATA(n)->theta)
 
-extern void patchwork_init_graph(graph_t * g);
-extern void twopi_layout(Agraph_t * g);
-extern void twopi_cleanup(Agraph_t * g);
+extern void patchwork_layout(Agraph_t * g);
+extern void patchwork_cleanup(Agraph_t * g);
+extern void patchworkLayout(Agraph_t *g);
 
 #ifdef __cplusplus
 }
index 443555f588a3469fc4c130cb0ea047db41654fc8..8dc29871b6a8fa1f93574fe3376520b7a02d7c75 100644 (file)
@@ -1,3 +1,4 @@
+/* $Id$Revision: */
 /* vim:set shiftwidth=4 ts=8: */
 
 /*************************************************************************
@@ -10,7 +11,6 @@
  * Contributors: See CVS logs. Details at http://www.graphviz.org/
  *************************************************************************/
 
-
 #include    "patchwork.h"
 #include    "adjust.h"
 #include    "pack.h"
@@ -108,16 +108,12 @@ mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
 static void patchwork_init_node(node_t * n)
 {
     agset(n,"shape","box");
-    common_init_node_opt(n,FALSE);
-    /* gv_nodesize(n, GD_flip(agraphof(n))); */
-    /* ND_pos(n) = ALLOC(GD_ndim(agraphof(n)), 0, double); */
+    /* common_init_node_opt(n,FALSE); */
 }
 
 static void patchwork_init_edge(edge_t * e)
 {
     /* common_init_edge(e); */
-
-    ED_factor(e) = late_double(e, E_weight, 1.0, 0.0);
 }
 
 static void patchwork_init_node_edge(graph_t * g)
@@ -140,7 +136,7 @@ static void patchwork_init_node_edge(graph_t * g)
     }
 }
 
-void patchwork_init_graph(graph_t * g)
+static void patchwork_init_graph(graph_t * g)
 {
 #ifndef WITH_CGRAPH
     N_shape = agnodeattr(g, "shape", "box");
@@ -154,6 +150,21 @@ void patchwork_init_graph(graph_t * g)
     patchwork_init_node_edge(g);
 }
 
+/* patchwork_layout:
+ * The current version makes no use of edges, neither for a notion of connectivity
+ * nor during drawing.
+ */
+void patchwork_layout(Agraph_t *g)
+{
+    if (agnnodes(g) == 0) return;
+
+    patchwork_init_graph(g);
+
+    patchworkLayout (g);
+
+    dotneato_postprocess(g);
+}
+
 static void patchwork_cleanup_graph(graph_t * g)
 {
     free(GD_neato_nlist(g));
@@ -170,7 +181,10 @@ void patchwork_cleanup(graph_t * g)
     node_t *n;
     edge_t *e;
 
-    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+    n = agfstnode(g);
+    if (!n) return;
+    free (ND_alg(n));
+    for (; n; n = agnxtnode(g, n)) {
        for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
            gv_cleanup_edge(e);
        }
diff --git a/lib/patchwork/tree_map.c b/lib/patchwork/tree_map.c
new file mode 100644 (file)
index 0000000..baa2073
--- /dev/null
@@ -0,0 +1,127 @@
+/* $Id$Revision: */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include "render.h"
+#include "tree_map.h"
+
+static void squarify(int n, real *area, rectangle *recs, int nadded, real maxarea, real minarea, real totalarea,
+                    real asp, rectangle fillrec){
+  /* add a list of area in fillrec using squarified treemap alg.
+     n: number of items to add
+     area: area of these items, Sum to 1 (?).
+     nadded: number of items already added
+     maxarea: maxarea of already added items
+     minarea: min areas of already added items
+     asp: current worst aspect ratio of the already added items so far
+     fillrec: the rectangle to be filled in.
+   */
+  real w = MIN(fillrec.size[0], fillrec.size[1]);
+  int i;
+
+  if (n <= 0) return;
+
+  if (Verbose) {
+    fprintf(stderr, "trying to add to rect {%f +/- %f, %f +/- %f}\n",fillrec.x[0], fillrec.size[0], fillrec.x[1], fillrec.size[1]);
+    fprintf(stderr, "total added so far = %d\n", nadded);
+  }
+
+  if (nadded == 0){
+    nadded = 1;
+    maxarea = minarea = area[0];
+    asp = MAX(area[0]/(w*w), (w*w)/area[0]);
+    totalarea = area[0];
+    squarify(n, area, recs, nadded, maxarea, minarea, totalarea, asp, fillrec);
+  } else {
+    real newmaxarea, newminarea, s, h, maxw, minw, newasp, hh, ww, xx, yy;
+    if (nadded < n){
+      newmaxarea = MAX(maxarea, area[nadded]);
+      newminarea = MIN(minarea, area[nadded]);
+      s = totalarea + area[nadded];
+      h = s/w;
+      maxw = newmaxarea/h;
+      minw = newminarea/h;
+      newasp = MAX(h/minw, maxw/h);/* same as MAX{s^2/(w^2*newminarea), (w^2*newmaxarea)/(s^2)}*/
+    }
+    if (nadded < n && newasp <= asp){/* aspectio improved, keep adding */
+      squarify(n, area, recs, ++nadded, newmaxarea, newminarea, s, newasp, fillrec);
+    } else {
+      /* aspectio worsen if add another area, fixed the already added recs */
+      if (Verbose) fprintf(stderr,"adding %d items, total area = %f, w = %f, area/w=%f\n",nadded, totalarea, w, totalarea/w);
+      if (w == fillrec.size[0]){/* tall rec. fix the items along x direction, left to right, at top*/
+       hh = totalarea/w;
+       xx = fillrec.x[0] - fillrec.size[0]/2;
+       for (i = 0; i < nadded; i++){
+         recs[i].size[1] = hh;
+         ww = area[i]/hh;
+         recs[i].size[0] = ww;
+         recs[i].x[1] = fillrec.x[1] + 0.5*(fillrec.size[1]) - hh/2;
+         recs[i].x[0] = xx + ww/2;
+         xx += ww;
+       }
+       fillrec.x[1] -= hh/2;/* the new empty space is below the filled space */
+       fillrec.size[1] -= hh;
+      } else {/* short rec. fix along y top to bot, at left*/
+       ww = totalarea/w;
+       yy = fillrec.x[1] + fillrec.size[1]/2;
+       for (i = 0; i < nadded; i++){
+         recs[i].size[0] = ww;
+         hh = area[i]/ww;
+         recs[i].size[1] = hh;
+         recs[i].x[0] = fillrec.x[0] - 0.5*(fillrec.size[0]) + ww/2;
+         recs[i].x[1] = yy - hh/2;
+         yy -= hh;
+       }
+       fillrec.x[0] += ww/2;/* the new empty space is right of the filled space */
+       fillrec.size[0] -= ww;
+      }
+      squarify(n - nadded, area + nadded, recs + nadded, 0, 0., 0., 0., 1., fillrec);
+    }
+
+  }
+}
+
+/* tree_map:
+ * Perform a squarified treemap layout on a single level.
+ *  n - number of rectangles
+ *  area - area of rectangles
+ *  fillred - rectangle to be filled
+ *  return array of rectangles 
+ */
+rectangle* tree_map(int n, real *area, rectangle fillrec){
+  /* fill a rectangle rec with n items, each item i has area[i] area. */
+  rectangle *recs;
+  int i;
+  real total = 0, minarea = 1., maxarea = 0., asp = 1, totalarea = 0;
+  int nadded = 0;
+
+  for (i = 0; i < n; i++) total += area[i]; 
+    /* make sure there is enough area */
+  if (total > fillrec.size[0] * fillrec.size[1] + 0.001)
+    return NULL;
+  
+  recs = N_NEW(n,rectangle);
+  squarify(n, area, recs, nadded, maxarea, minarea, totalarea, asp, fillrec);
+  return recs;
+}
+
+/* rectangle_new:
+ * Create and initialize a new rectangle structure
+ */
+rectangle rectangle_new(real x, real y, real width, real height){
+  rectangle r;
+  r.x[0] = x;
+  r.x[1] = y;
+  r.size[0] = width;
+  r.size[1] = height;
+  return r;
+}
diff --git a/lib/patchwork/tree_map.h b/lib/patchwork/tree_map.h
new file mode 100644 (file)
index 0000000..12bc904
--- /dev/null
@@ -0,0 +1,28 @@
+/* $Id$Revision: */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef TREE_MAP_H
+#define TREE_MAP_H
+
+#include <SparseMatrix.h>
+
+typedef struct rectangle_struct {
+  real x[2];/* center */
+  real size[2]; /* total width/height*/
+} rectangle;
+
+extern rectangle* tree_map(int n, real *area, rectangle fillrec);
+
+extern rectangle rectangle_new(real x, real y, real width, real height);
+
+#endif