Graphviz  2.29.20120524.0446
lib/fdpgen/grid.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 /*
00016  * grid.c
00017  * Written by Emden R. Gansner
00018  *
00019  * Support for grid to speed up layout. On each pass, nodes are
00020  * put into grid cells. Given a node, repulsion is only computed 
00021  * for nodes in one of that nodes 9 adjacent grids.
00022  */
00023 
00024 /* uses PRIVATE interface for NOTUSED */
00025 #define FDP_PRIVATE 1
00026 
00027 #include <fdp.h>
00028 #include <grid.h>
00029 #include <macros.h>
00030 
00031   /* structure for maintaining a free list of cells */
00032 typedef struct _block {
00033     cell *mem;                  /* block of cells */
00034     cell *cur;                  /* next available cell */
00035     cell *endp;                 /* after last cell */
00036     struct _block *next;        /* next memory block */
00037 } block_t;
00038 
00039 /* newBlock:
00040  * Create new block of size cells
00041  */
00042 static block_t *newBlock(int size)
00043 {
00044     block_t *newb;
00045 
00046     newb = GNEW(block_t);
00047     newb->next = 0;
00048     newb->mem = N_GNEW(size, cell);
00049     newb->endp = newb->mem + size;
00050     newb->cur = newb->mem;
00051 
00052     return newb;
00053 }
00054 
00055 /* freeBlock:
00056  * Free malloc'ed memory and block.
00057  * Recurse to next block
00058  */
00059 static void freeBlock(block_t * b)
00060 {
00061     if (b) {
00062         block_t *next = b->next;
00063         free(b->mem);
00064         free(b);
00065         freeBlock(next);
00066     }
00067 }
00068 
00069 struct _grid {
00070     Dt_t *data;                 /* cells indexed by (i,j) */
00071     block_t *cellMem;           /* list of memory blocks for cells */
00072     block_t *cellCur;           /* current block */
00073     int listSize;               /* memory of nodes */
00074     node_list *listMem;         /* list of memory for node items */
00075     node_list *listCur;         /* next node item */
00076 };
00077 
00078 /* getCell:
00079  * Create a new cell using memory blocks.
00080  */
00081 static cell *getCell(Grid * g)
00082 {
00083     cell *cp;
00084     block_t *bp = g->cellCur;   /* current block */
00085 
00086     if (bp->cur == bp->endp) {  /* current block is full */
00087         if (bp->next == 0) {
00088             bp->next = newBlock(2 * (bp->endp - bp->mem));
00089         }
00090         bp = g->cellCur = bp->next;
00091         bp->cur = bp->mem;
00092     }
00093     cp = bp->cur++;
00094     return cp;
00095 }
00096 
00097 #ifndef offsetof
00098 #define offsetof(typ,fld)  ((int)(&(((typ*)0)->fld)))
00099 #endif
00100 
00101 
00102 static int ijcmpf(Dt_t * d, gridpt * p1, gridpt * p2, Dtdisc_t * disc)
00103 {
00104     int diff;
00105 
00106     NOTUSED(d);
00107     NOTUSED(disc);
00108     if ((diff = (p1->i - p2->i)))
00109         return diff;
00110     else
00111         return (p1->j - p2->j);
00112 }
00113 
00114 static Grid *_grid;             /* hack because can't attach info. to Dt_t */
00115 
00116 /* newCell:
00117  * Allocate a new cell from free store and initialize its indices
00118  * This is used by the grid discipline to create cells.
00119  */
00120 static void *newCell(Dt_t * d, void *obj, Dtdisc_t * disc)
00121 {
00122     cell *cellp = (cell *) obj;
00123     cell *newp;
00124 
00125     NOTUSED(disc);
00126     newp = getCell(_grid);
00127     newp->p.i = cellp->p.i;
00128     newp->p.j = cellp->p.j;
00129     newp->nodes = 0;
00130 
00131     return newp;
00132 }
00133 
00134 /* newNode:
00135  * Allocate a new node item from free store. 
00136  * Set node value and hook into list.
00137  * A grid assumes the memory allocated in adjustGrid
00138  * will be enough more all nodes added.
00139  */
00140 static node_list *newNode(Grid * g, Agnode_t * n, node_list * nxt)
00141 {
00142     node_list *newp;
00143 
00144     newp = g->listCur++;
00145     newp->node = n;
00146     newp->next = nxt;
00147 
00148     return newp;
00149 }
00150 
00151 static Dtdisc_t gridDisc = {
00152     offsetof(cell, p),
00153     sizeof(gridpt),
00154     offsetof(cell, link),
00155     (Dtmake_f) newCell,
00156     NIL(Dtfree_f),
00157     (Dtcompar_f) ijcmpf,
00158     NIL(Dthash_f),
00159     NIL(Dtmemory_f),
00160     NIL(Dtevent_f)
00161 };
00162 
00163 /* mkGrid:
00164  * Create grid data structure.
00165  * cellHint provides rough idea of how many cells
00166  * may be needed.
00167  */
00168 Grid *mkGrid(int cellHint)
00169 {
00170     Grid *g;
00171 
00172     g = GNEW(Grid);
00173     _grid = g;                  /* see comment above */
00174     g->data = dtopen(&gridDisc, Dtoset);
00175     g->listMem = 0;
00176     g->listSize = 0;
00177     g->cellMem = newBlock(cellHint);
00178     return g;
00179 }
00180 
00181 /* adjustGrid:
00182  * Set up node list for grid. Make sure the list
00183  * can handle nnodes nodes.
00184  * It is assumed no more than nnodes will be added
00185  * to the grid.
00186  */
00187 void adjustGrid(Grid * g, int nnodes)
00188 {
00189     int nsize;
00190 
00191     if (nnodes > g->listSize) {
00192         nsize = MAX(nnodes, 2 * (g->listSize));
00193         if (g->listMem)
00194             free(g->listMem);
00195         g->listMem = N_GNEW(nsize, node_list);
00196         g->listSize = nsize;
00197     }
00198 }
00199 
00200 /* clearGrid:
00201  * Reset grid. This clears the dictionary,
00202  * and reuses available memory.
00203  */
00204 void clearGrid(Grid * g)
00205 {
00206     dtclear(g->data);
00207     g->listCur = g->listMem;
00208     g->cellCur = g->cellMem;
00209     g->cellCur->cur = g->cellCur->mem;
00210 }
00211 
00212 /* delGrid:
00213  * Close and free all grid resources.
00214  */
00215 void delGrid(Grid * g)
00216 {
00217     dtclose(g->data);
00218     freeBlock(g->cellMem);
00219     free(g->listMem);
00220     free(g);
00221 }
00222 
00223 /* addGrid:
00224  * Add node n to cell (i,j) in grid g.
00225  */
00226 void addGrid(Grid * g, int i, int j, Agnode_t * n)
00227 {
00228     cell *cellp;
00229     cell key;
00230 
00231     key.p.i = i;
00232     key.p.j = j;
00233     cellp = dtinsert(g->data, &key);
00234     cellp->nodes = newNode(g, n, cellp->nodes);
00235     if (Verbose >= 3) {
00236         fprintf(stderr, "grid(%d,%d): %s\n", i, j, agnameof(n));
00237     }
00238 }
00239 
00240 typedef int (*walkfn_t) (Dt_t *, Void_t *, Void_t *);
00241 
00242 /* walkGrid:
00243  * Apply function walkf to each cell in the grid.
00244  * The second argument to walkf is the cell; the
00245  * third argument is the grid. (The first argument
00246  * is the dictionary.) walkf must return 0.
00247  */
00248 void walkGrid(Grid * g, int (*walkf) (Dt_t *, cell *, Grid *))
00249 {
00250     dtwalk(g->data, (walkfn_t) walkf, g);
00251 }
00252 
00253 /* findGrid;
00254  * Return the cell, if any, corresponding to
00255  * indices i,j
00256  */
00257 cell *findGrid(Grid * g, int i, int j)
00258 {
00259     cell key;
00260 
00261     key.p.i = i;
00262     key.p.j = j;
00263     return ((cell *) dtsearch(g->data, &key));
00264 }
00265 
00266 /* gLength:
00267  * Return the number of nodes in a cell.
00268  */
00269 int gLength(cell * p)
00270 {
00271     int len = 0;
00272     node_list *nodes = p->nodes;
00273 
00274     while (nodes) {
00275         len++;
00276         nodes = nodes->next;
00277     }
00278     return len;
00279 }