--- /dev/null
+# $Id$ $Revision$
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/lib/common \
+ -I$(top_srcdir)/lib/gvc \
+ -I$(top_srcdir)/lib/neatogen \
+ -I$(top_srcdir)/lib/pack \
+ -I$(top_srcdir)/lib/pathplan \
+ -I$(top_srcdir)/lib/graph \
+ -I$(top_srcdir)/lib/cdt
+
+noinst_HEADERS = patchwork.h
+noinst_LTLIBRARIES = libpatchwork_C.la
+
+libpatchwork_C_la_SOURCES = patchwork.c patchworkinit.c
+
+EXTRA_DIST = Makefile.old
--- /dev/null
+all: libtwopigen.a
+ROOT=../..
+include $(ROOT)/Config.mk
+include $(ROOT)/makearch/$(ARCH)
+
+INCS = -I. -I$(ROOT) \
+ -I../common \
+ -I../neatogen \
+ -I../pack \
+ -I../gvc \
+ -I../pathplan \
+ -I../graph \
+ -I../cdt \
+ -I../gd
+
+DEFINES = -DHAVE_CONFIG_H
+
+OBJS = twopiinit.o circle.o
+
+HFILES = circle.h
+
+libtwopigen.a : $(OBJS)
+ $(RM) libtwopigen.a
+ $(AR) cr libtwopigen.a $(OBJS)
+ $(RANLIB) libtwopigen.a
+
+$(OBJS) : $(HFILES) ../common/types.h
+
+install: libtwopigen.a
+ $(MKPATH) $(LIBDIR)
+ $(INSTALL) libtwopigen.a $(LIBDIR)
+
+clean:
+ $(RM) *.o core
+
+distclean: clean
+ $(RM) *.a twopi lib*.so.*
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "render.h"
+
+typedef struct rect_t {pointf ll; pointf ur;} 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
+
+static treenode_t *newtreenode(treenode_t **first, treenode_t **prev)
+{
+ treenode_t *p;
+ p = malloc(sizeof(treenode_t));
+ p->leftchild = p->rightsib = 0;
+ p->area = 0.0;
+ if (!*first) *first = p;
+ if (*prev) (*prev)->rightsib = p;
+ *prev = p;
+ return p;
+}
+
+/* return list of treenodes */
+static treenode_t *treebuilder(Agraph_t *g)
+{
+ int i;
+ treenode_t *first = 0;
+ treenode_t *prev = 0;
+ treenode_t *p;
+ Agraph_t *subg;
+ Agnode_t *n;
+
+ for (i = 1; i <= GD_n_cluster(g); i++) {
+ subg = GD_clust(g)[i];
+ if (agnnodes(subg) == 0) continue;
+ p = newtreenode(&first,&prev);
+ p->kind = AGRAPH;
+ p->u.subg = subg;
+ p->leftchild = treebuilder(subg);
+ }
+
+ for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
+ char *val;
+ 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;
+ }
+ return first;
+}
+
+/* get recursive area requirements */
+static double sizeit(treenode_t *tree)
+{
+ treenode_t *p;
+ double mysize = 0.0;
+
+ if (tree->leftchild) {
+ for (p = tree->leftchild; p; p = p->rightsib) mysize += sizeit(p);
+ tree->area = mysize;
+ }
+ return tree->area;
+}
+
+/* normal layout */
+static void layouter(treenode_t *tree, int dir, rect_t r)
+{
+ double delta;
+ pointf ref;
+ rect_t r0;
+ treenode_t *p;
+
+ tree->r = r;
+ if (dir == BT) delta = (r.ur.y - r.ll.y) / tree->area;
+ else if (dir == LR) delta = (r.ur.x - r.ll.x) / tree->area;
+ else abort();
+ ref = r.ll;
+ for (p = tree->leftchild; p; p = p->rightsib) {
+ r0.ll = ref;
+ if (dir == BT) {
+ r0.ur.x = r.ur.x;
+ r0.ur.y = ref.y + p->area * delta;
+ }
+ else {
+ r0.ur.x = ref.x + p->area * delta;
+ r0.ur.y = r.ur.y;
+ }
+ layouter(p,(dir == BT? LR : BT),r0);
+ if (dir == BT) ref.y = r0.ur.y;
+ else ref.x = r0.ur.x;
+ }
+}
+
+/* squarified layout */
+static double aspect(rect_t r) { return (r.ur.y - r.ll.y)/(r.ur.x - r.ll.x); }
+
+static void sqlayouter(treenode_t *list, int dir, rect_t r, double total)
+{
+ double frac = list->area / total;
+ rect_t s = r;
+ if (dir == BT) s.ur.y = s.ll.y + frac * (r.ur.y - r.ll.y);
+ else s.ur.x = s.ll.x + frac * (r.ur.x - r.ll.x);
+ list->r = s;
+
+ if (list->leftchild) {
+ if (aspect(r) > 1) sqlayouter(list->leftchild, BT, r, list->area);
+ else sqlayouter(list->leftchild, LR,r, list->area);
+ }
+
+ if (list->rightsib) {
+ total = total - list->area;
+ if (dir == BT) r.ll.y = s.ur.y;
+ else r.ll.x = s.ur.x;
+ if (aspect(r) > 1) sqlayouter(list->rightsib, BT, r, total);
+ else sqlayouter(list->rightsib, LR, r, total);
+ }
+}
+
+static void printer(treenode_t *tree)
+{
+ static int onetime = 1;
+ treenode_t *p;
+
+ 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());
+ }
+}
+
+static void walker(treenode_t *tree)
+{
+ treenode_t *p;
+ Agnode_t *n;
+ point center;
+
+ switch(tree->kind) {
+ case AGRAPH:
+ break;
+ case AGNODE:
+ center.x = (tree->r.ur.x + tree->r.ll.x) / 2.0;
+ center.y = (tree->r.ur.y + tree->r.ll.y) / 2.0;
+
+ n = tree->u.n;
+ ND_coord_i(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(n->graph));
+ /*fprintf(stderr,"%s coord %d %d ht %d width %d\n",
+ n->name, ND_coord_i(n).x, ND_coord_i(n).y, ND_ht_i(n),
+ ND_rw_i(n)+ND_lw_i(n));*/
+ break;
+ default: abort();
+ }
+ if (tree->leftchild)
+ for (p = tree->leftchild; p; p = p->rightsib) walker(p);
+}
+
+#ifdef PWDRIVER
+int main()
+{
+ static treenode_t root;
+ rect_t r = {{0.0, 0.0}, {100.0, 100.0}};
+ Agraph_t *g;
+
+ aginit();
+ g = agread(stdin);
+ root.leftchild = treebuilder(g);
+ sizeit(&root);
+ /*layouter(&root,LR,r);*/
+ sqlayouter(&root,LR,r,root.area);
+ printer(&root);
+ return 0;
+}
+#endif
+
+void patchwork_layout(Agraph_t *g)
+{
+ static treenode_t root;
+ rect_t r = {{0.0, 0.0}, {100.0, 100.0}};
+ patchwork_init_graph(g);
+ root.leftchild = treebuilder(g);
+ sizeit(&root);
+ sqlayouter(&root,LR,r,root.area);
+ 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);
+}
--- /dev/null
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+* This software is part of the graphviz package *
+* http://www.graphviz.org/ *
+* *
+* Copyright (c) 1994-2004 AT&T Corp. *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Corp. *
+* *
+* Information and Software Systems Research *
+* AT&T Research, Florham Park NJ *
+**********************************************************/
+
+#ifndef PATCHWORK_H
+#define PATCHWORK_H
+
+#include "render.h"
+#include "../fdpgen/fdp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef struct {
+ int nStepsToLeaf;
+ int subtreeSize;
+ int nChildren;
+ int nStepsToCenter;
+ node_t *parent;
+ double span;
+ double theta;
+ } rdata;
+
+#define RDATA(n) ((rdata*)((n)->u.alg))
+#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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+* This software is part of the graphviz package *
+* http://www.graphviz.org/ *
+* *
+* Copyright (c) 1994-2004 AT&T Corp. *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Corp. *
+* *
+* Information and Software Systems Research *
+* AT&T Research, Florham Park NJ *
+**********************************************************/
+
+
+#include "patchwork.h"
+#include "adjust.h"
+#include "pack.h"
+#include "neatoprocs.h"
+
+/* the following code shamelessly copied from lib/fdpgen/layout.c
+and should be extracted and made into a common function */
+
+#define CL_CHUNK 10
+
+typedef struct {
+ graph_t **cl;
+ int sz;
+ int cnt;
+} clist_t;
+
+static void initCList(clist_t * clist)
+{
+ clist->cl = 0;
+ clist->sz = 0;
+ clist->cnt = 0;
+}
+
+/* addCluster:
+ * Append a new cluster to the list.
+ * NOTE: cl[0] is empty. The clusters are in cl[1..cnt].
+ * Normally, we increase the array when cnt == sz.
+ * The test for cnt > sz is necessary for the first time.
+ */
+static void addCluster(clist_t * clist, graph_t * subg)
+{
+ clist->cnt++;
+ if (clist->cnt >= clist->sz) {
+ clist->sz += CL_CHUNK;
+ clist->cl = RALLOC(clist->sz, clist->cl, graph_t *);
+ }
+ clist->cl[clist->cnt] = subg;
+}
+
+/* mkClusters:
+ * Attach list of immediate child clusters.
+ * NB: By convention, the indexing starts at 1.
+ * If pclist is NULL, the graph is the root graph or a cluster
+ * If pclist is non-NULL, we are recursively scanning a non-cluster
+ * subgraph for cluster children.
+ */
+static void
+mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
+{
+ node_t* mn;
+ edge_t* me;
+ graph_t* mg;
+ graph_t* subg;
+ clist_t list;
+ clist_t* clist;
+
+ if (pclist == NULL) {
+ clist = &list;
+ initCList(clist);
+ }
+ else
+ clist = pclist;
+ mg = g->meta_node->graph;
+ for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
+ mn = me->head;
+ subg = agusergraph(mn);
+ if (!strncmp(subg->name, "cluster", 7)) {
+#ifdef FDP_GEN
+ GD_alg(subg) = (void *) NEW(gdata); /* freed in cleanup_subgs */
+ GD_ndim(subg) = GD_ndim(parent);
+ LEVEL(subg) = LEVEL(parent) + 1;
+ GPARENT(subg) = parent;
+#endif
+ addCluster(clist, subg);
+ mkClusters(subg, NULL, subg);
+ }
+ else {
+ mkClusters(subg, clist, parent);
+ }
+ }
+ if (pclist == NULL) {
+ GD_n_cluster(g) = list.cnt;
+ if (list.cnt)
+ GD_clust(g) = RALLOC(list.cnt + 1, list.cl, graph_t*);
+ }
+}
+
+static void patchwork_init_node(node_t * n)
+{
+ agset(n,"shape","box");
+ common_init_node(n);
+ gv_nodesize(n, GD_flip(n->graph));
+ ND_pos(n) = ALLOC(GD_ndim(n->graph), 0, double);
+}
+
+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)
+{
+ node_t *n;
+ edge_t *e;
+ int i = 0;
+ rdata* alg = N_NEW(agnnodes(g), rdata);
+
+ GD_neato_nlist(g) = N_NEW(agnnodes(g) + 1, node_t *);
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ ND_alg(n) = alg + i;
+ GD_neato_nlist(g)[i++] = n;
+ patchwork_init_node(n);
+ }
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+ patchwork_init_edge(e);
+ }
+ }
+}
+
+void patchwork_init_graph(graph_t * g)
+{
+ agnodeattr(g,"shape","box");
+ setEdgeType (g, ET_LINE);
+ /* GD_ndim(g) = late_int(g,agfindattr(g,"dim"),2,2); */
+ Ndim = GD_ndim(g) = 2; /* The algorithm only makes sense in 2D */
+ mkClusters(g, NULL, g);
+ patchwork_init_node_edge(g);
+}
+
+static void patchwork_cleanup_graph(graph_t * g)
+{
+ free(GD_neato_nlist(g));
+ if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
+}
+
+void patchwork_cleanup(graph_t * g)
+{
+ node_t *n;
+ edge_t *e;
+
+ for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+ for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+ gv_cleanup_edge(e);
+ }
+ gv_cleanup_node(n);
+ }
+ patchwork_cleanup_graph(g);
+}