/* -*- mode: C -*- */ /* IGraph R package. Copyright (C) 2003, 2004, 2005, 2006 Gabor Csardi MTA RMKI, Konkoly-Thege Miklos st. 29-33, Budapest 1121, Hungary This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph.h" #include "random.h" #include "memory.h" #include int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *r); int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *z, igraph_real_t *r); /** * \section about_layouts * * Layout generator functions (or at least most of them) try to place the * vertices and edges of a graph on a 2D plane or in 3D space in a way * which visually pleases the human eye. * * They take a graph object and a number of parameters as arguments * and return an \type igraph_matrix_t, in which each row gives the * coordinates of a vertex. */ /** * \ingroup layout * \function igraph_layout_random * \brief Places the vertices uniform randomly on a plane. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized graph object. This will * contain the result and will be resized in needed. * \return Error code. The current implementation always returns with * success. * * Time complexity: O(|V|), the * number of vertices. */ int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); RNG_BEGIN(); for (i=0; i * * Time complexity: O(|V|), the number of vertices. */ int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); RNG_BEGIN(); for (i=0; i * The algorithm was described in the following paper: * Distributing many points on a sphere by E.B. Saff and * A.B.J. Kuijlaars, \emb Mathematical Intelligencer \eme 19.1 (1997) * 5--11. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object, the will be * stored here. It will be resized. * \return Error code. The current implementation always returns with * success. * * Added in version 0.2. * * Time complexity: O(|V|), the number of vertices in the graph. */ int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { long int no_of_nodes=igraph_vcount(graph); long int i; igraph_real_t h; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); if (no_of_nodes != 0) { MATRIX(*res, 0, 0)=M_PI; MATRIX(*res, 0, 1)=0; } for (i=1; i=2) { MATRIX(*res, no_of_nodes-1, 0)=0; MATRIX(*res, no_of_nodes-1, 1)=0; } for (i=0; i * This is a force-directed layout, see Fruchterman, T.M.J. and * Reingold, E.M.: Graph Drawing by Force-directed Placement. * Software -- Practice and Experience, 21/11, 1129--1164, * 1991. * This function was ported from the SNA R package. * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized in needed. * \param niter The number of iterations to do. * \param maxdelta The maximum distance to move a vertex in an * iteration. * \param area The area parameter of the algorithm. * \param coolexp The cooling exponent of the simulated annealing. * \param repulserad Determines the radius at which * vertex-vertex repulsion cancels out attraction of * adjacent vertices. * \param use_seed Logical, if true the supplied values in the * \p res argument are used as an initial layout, if * false a random initial layout is used. * \return Error code. * * Time complexity: O(|V|^2) in each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed) { igraph_real_t frk,t,ded,xd,yd; igraph_real_t rf,af; long int i,j,k; long int no_of_nodes=igraph_vcount(graph); igraph_matrix_t dxdy=IGRAPH_MATRIX_NULL; igraph_eit_t edgeit; igraph_integer_t from, to; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); if (!use_seed) { IGRAPH_CHECK(igraph_layout_random(graph, res)); } IGRAPH_MATRIX_INIT_FINALLY(&dxdy, no_of_nodes, 2); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); frk=sqrt(area/no_of_nodes); for(i=niter;i>0;i--) { /* Report progress in approx. every 100th step */ if (i%10 == 0) igraph_progress("Fruchterman-Reingold layout: ", 100.0-100.0*i/niter, NULL); /* Set the temperature (maximum move/iteration) */ t=maxdelta*pow(i/(double)niter,coolexp); /* Clear the deltas */ igraph_matrix_null(&dxdy); /* Increment deltas for each undirected pair */ for(j=0;jt){ /* Dampen to t */ ded=t/ded; MATRIX(dxdy, j, 0)*=ded; MATRIX(dxdy, j, 1)*=ded; } MATRIX(*res, j, 0)+=MATRIX(dxdy, j, 0); /* Update positions */ MATRIX(*res, j, 1)+=MATRIX(dxdy, j, 1); } } igraph_progress("Fruchterman-Reingold layout: ", 100.0, NULL); igraph_eit_destroy(&edgeit); igraph_matrix_destroy(&dxdy); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \function igraph_layout_fruchterman_reingold_3d * \brief This is the 3D version of the force based * Fruchterman-Reingold layout (see \ref * igraph_layout_fruchterman_reingold for the 2D version * * * This function was ported from the SNA R package. * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized in needed. * \param niter The number of iterations to do. * \param maxdelta The maximum distance to move a vertex in an * iteration. * \param volume The volume parameter of the algorithm. * \param coolexp The cooling exponent of the simulated annealing. * \param repulserad Determines the radius at which * vertex-vertex repulsion cancels out attraction of * adjacent vertices. * \param use_seed Logical, if true the supplied values in the * \p res argument are used as an initial layout, if * false a random initial layout is used. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|^2) in each * iteration, |V| is the number of * vertices in the graph. * */ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t volume, igraph_real_t coolexp, igraph_real_t repulserad, igraph_bool_t use_seed) { igraph_real_t frk, t, ded, xd, yd, zd; igraph_matrix_t dxdydz; igraph_real_t rf, af; long int i, j, k; long int no_of_nodes=igraph_vcount(graph); igraph_eit_t edgeit; igraph_integer_t from, to; IGRAPH_CHECK(igraph_matrix_init(&dxdydz, no_of_nodes, 3)); IGRAPH_FINALLY(igraph_matrix_destroy, &dxdydz); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); if (!use_seed) { IGRAPH_CHECK(igraph_layout_random_3d(graph, res)); } IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); frk=pow(volume/(double)no_of_nodes,1.0/3.0); /*Define the F-R constant*/ /*Run the annealing loop*/ for(i=niter;i>=0;i--){ if (i%10 == 0) igraph_progress("3D Fruchterman-Reingold layout: ", 100.0-100.0*i/niter, NULL); /*Set the temperature (maximum move/iteration)*/ t=maxdelta*pow(i/(double)niter,coolexp); /*Clear the deltas*/ igraph_matrix_null(&dxdydz); /*Increment deltas for each undirected pair*/ for(j=0;jt){ /*Dampen to t*/ ded=t/ded; MATRIX(dxdydz, j, 0)*=ded; MATRIX(dxdydz, j, 1)*=ded; MATRIX(dxdydz, j, 2)*=ded; } MATRIX(*res, j, 0)+=MATRIX(dxdydz, j, 0); /*Update positions*/ MATRIX(*res, j, 1)+=MATRIX(dxdydz, j, 1); MATRIX(*res, j, 2)+=MATRIX(dxdydz, j, 2); } } igraph_progress("3D Fruchterman-Reingold layout: ", 100.0, NULL); igraph_matrix_destroy(&dxdydz); igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(2); return 0; } /** * \ingroup layout * \function igraph_layout_kamada_kawai * \brief Places the vertices on a plane according the Kamada-Kawai * algorithm. * * * This is a force directed layout, see Kamada, T. and Kawai, S.: An * Algorithm for Drawing General Undirected Graphs. Information * Processing Letters, 31/1, 7--15, 1989. * This function was ported from the SNA R package. * \param graph A graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized if needed. * \param niter The number of iterations to perform. * \param sigma Sets the base standard deviation of position * change proposals. * \param initemp Sets the initial temperature for the annealing. * \param coolexp The cooling exponent of the annealing. * \param kkconst The Kamada-Kawai vertex attraction constant. * \return Error code. * * Time complexity: O(|V|^2) for each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst) { igraph_real_t temp, candx, candy; igraph_real_t dpot, odis, ndis, osqd, nsqd; long int n,i,j,k; igraph_matrix_t elen; /* Define various things */ n=igraph_vcount(graph); /* Calculate elen, initial x & y */ RNG_BEGIN(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); IGRAPH_MATRIX_INIT_FINALLY(&elen, n, n); IGRAPH_CHECK(igraph_shortest_paths(graph, &elen, igraph_vss_all(), IGRAPH_ALL)); for (i=0; i * This function was ported from the SNA R package. * \param graph A graph object. * \param res Pointer to an initialized matrix object. This will * contain the result and will be resized if needed. * \param niter The number of iterations to perform. * \param sigma Sets the base standard deviation of position * change proposals. * \param initemp Sets the initial temperature for the annealing. * \param coolexp The cooling exponent of the annealing. * \param kkconst The Kamada-Kawai vertex attraction constant. * \return Error code. * * Added in version 0.2. * * Time complexity: O(|V|^2) for each * iteration, |V| is the number of * vertices in the graph. */ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t sigma, igraph_real_t initemp, igraph_real_t coolexp, igraph_real_t kkconst) { igraph_real_t temp, candx, candy, candz; igraph_real_t dpot, odis, ndis, osqd, nsqd; long int i,j,k; long int no_of_nodes=igraph_vcount(graph); igraph_matrix_t elen; RNG_BEGIN(); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); IGRAPH_MATRIX_INIT_FINALLY(&elen, no_of_nodes, no_of_nodes); IGRAPH_CHECK(igraph_shortest_paths(graph, &elen, igraph_vss_all(), IGRAPH_ALL)); temp=initemp; for(i=0;i * This is a layout generator similar to the Large Graph Layout * algorithm and program * (http://bioinformatics.icmb.utexas.edu/lgl/). But unlike LGL, this * version uses a Fruchterman-Reingold style simulated annealing * algorithm for placing the vertices. The speedup is achived by * placing the vertices on a grid and calculating the repulsion only * for vertices which are closer to each other than a limit. * * \param graph The (initialized) graph object to place. * \param res Pointer to an initialized matrix object to hold the * result. It will be resized if needed. * \param maxit The maximum number of cooling iterations to perform * for each layout step. * \param maxdelta The maximum length of the move allowed for a vertex * in a single iteration. * \param area This parameter gives the area of the square on which * the vertices will be placed. * \param coolexp The cooling exponent. * \param repulserad Determines the radius at which vertex-vertex * repulsion cancels out attraction of adjacenct vertices. * \param cellsize The size of the grid cells, one side of the * square. * \param proot The root vertex, this is placed first, its neighbors * in the first iteration, second neighbors in the second, etc. If * negative then a random vertex is chosen. * \return Error code. * * Added in version 0.2. * * Time complexity: ideally O(dia*maxit*(|V|+|E|)), |V| is the number * of vertices, * dia is the diameter of the graph, worst case complexity is still * O(dia*maxit*(|V|^2+|E|)), this is the case when all vertices happen to be * in the same grid cell. */ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t maxit, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t proot) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_t mst; long int root; long int no_of_layers, actlayer=0; igraph_vector_t vids; igraph_vector_t layers; igraph_vector_t parents; igraph_vector_t edges; igraph_2dgrid_t grid; igraph_vector_t eids; igraph_vector_t forcex; igraph_vector_t forcey; igraph_real_t frk=sqrt(area/no_of_nodes); igraph_real_t H_n=0; IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst)); IGRAPH_FINALLY(igraph_destroy, &mst); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); /* Determine the root vertex, random pick right now */ if (proot < 0) { root=RNG_INTEGER(0, no_of_nodes-1); } else { root=proot; } /* Assign the layers */ IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); IGRAPH_VECTOR_INIT_FINALLY(&layers, 0); IGRAPH_VECTOR_INIT_FINALLY(&parents, 0); IGRAPH_CHECK(igraph_bfs(&mst, root, IGRAPH_ALL, &vids, &layers, &parents)); no_of_layers=igraph_vector_size(&layers)-1; /* We don't need the mst any more */ igraph_destroy(&mst); igraph_empty(&mst, 0, IGRAPH_UNDIRECTED); /* to make finalization work */ IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges)); IGRAPH_VECTOR_INIT_FINALLY(&eids, 0); IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); /* Place the vertices randomly */ IGRAPH_CHECK(igraph_layout_random(graph, res)); igraph_matrix_multiply(res, 1e6); /* This is the grid for calculating the vertices near to a given vertex */ IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize)); IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); /* Place the root vertex */ igraph_2dgrid_add(&grid, root, 0, 0); for (actlayer=1; actlayer epsilon) { long int j; igraph_real_t t=maxdelta*pow((maxit-it)/(double)maxit, coolexp); long int vid, nei; /* init */ igraph_vector_null(&forcex); igraph_vector_null(&forcey); maxchange=0; /* attractive "forces" along the edges */ for (j=0; j t) { ded=t/ded; fx*=ded; fy *=ded; } igraph_2dgrid_move(&grid, vid, fx, fy); if (fx > maxchange) { maxchange=fx; } if (fy > maxchange) { maxchange=fy; } } it++; /* printf("%li iterations, maxchange: %f\n", it, (double)maxchange); */ } } igraph_destroy(&mst); igraph_vector_destroy(&vids); igraph_vector_destroy(&layers); igraph_vector_destroy(&parents); igraph_vector_destroy(&edges); igraph_2dgrid_destroy(&grid); igraph_vector_destroy(&eids); igraph_vector_destroy(&forcex); igraph_vector_destroy(&forcey); IGRAPH_FINALLY_CLEAN(9); return 0; } /** * \function igraph_layout_grid_fruchterman_reingold * \brief Force based layout generator for large graphs. * * * This algorithm is the same as the Fruchterman-Reingold layout * generator, but it partitions the 2d space to a grid and and vertex * repulsion is calculated only for vertices nearby. * * \param graph The graph object. * \param res The result, the coordinates in a matrix. The parameter * should point to an initialized matrix object and will be resized. * \param niter Number of iterations to perform. * \param maxdelta Maximum distance to move a vertex in an iteration. * \param area The area of the square on which the vertices will be * placed. * \param coolexp The cooling exponent. * \param repulserad Determines the radius at which vertex-vertex * repulsion cancels out attraction of adjacenct vertices. * \param cellsize The size of the grid cells. * \param use_seed Logical, if true, the coordinates passed in \p res * (should have the appropriate size) will be used for the first * iteration. * \return Error code. * * Added in version 0.2. * * Time complexity: ideally (constant number of vertices in each cell) * O(niter*(|V|+|E|)), in the worst case O(niter*(|V|^2+|E|)). */ int igraph_layout_grid_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t niter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_bool_t use_seed) { long int no_of_nodes=igraph_vcount(graph); long int no_of_edges=igraph_ecount(graph); igraph_2dgrid_t grid; igraph_vector_t forcex; igraph_vector_t forcey; long int i, it=0; igraph_2dgrid_iterator_t vidit; igraph_real_t frk=sqrt(area/no_of_nodes); IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); /* initial layout */ if (!use_seed) { IGRAPH_CHECK(igraph_layout_random(graph, res)); igraph_matrix_multiply(res, sqrt(area/M_PI)); } /* make grid */ IGRAPH_CHECK(igraph_2dgrid_init(&grid, res, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize, -sqrt(area/M_PI),sqrt(area/M_PI), cellsize)); IGRAPH_FINALLY(igraph_2dgrid_destroy, &grid); /* place vertices on grid */ for (i=0; i t) { ded=t/ded; fx*=ded; fy *=ded; } igraph_2dgrid_move(&grid, vid, fx, fy); } it++; } /* it * Arranges the nodes in a tree where the given node is used as the root. * The tree is directed downwards and the parents are centered above its * children. For the exact algorithm, see: * * * Reingold, E and Tilford, J: Tidier drawing of trees. * IEEE Trans. Softw. Eng., SE-7(2):223--228, 1981 * * * If the given graph is not a tree, a breadth-first search is executed * first to obtain a possible spanning tree. * * \param graph The graph object. * \param res The result, the coordinates in a matrix. The parameter * should point to an initialized matrix object and will be resized. * \param root The index of the root vertex. * \return Error code. * * Added in version 0.2. * * * TODO: decompose and merge for not fully connected graphs * TODO: possible speedup could be achieved if we use a table for storing * the children of each node in the tree. (Now the implementation uses a * single array containing the parent of each node and a node's children * are determined by looking for other nodes that have this node as parent) */ int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, long int root) { long int no_of_nodes=igraph_vcount(graph); long int i, n, j; igraph_dqueue_t q=IGRAPH_DQUEUE_NULL; igraph_i_adjlist_t allneis; igraph_vector_t *neis; struct igraph_i_reingold_tilford_vertex *vdata; if (root<0 || root>=no_of_nodes) { IGRAPH_ERROR("invalid vertex id", IGRAPH_EINVVID); } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_i_adjlist_init(graph, &allneis, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_i_adjlist_destroy, &allneis); vdata=Calloc(no_of_nodes, struct igraph_i_reingold_tilford_vertex); if (vdata==0) { IGRAPH_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vdata); for (i=0; i= 0) { continue; } MATRIX(*res, neighbor, 1)=actdist+1; IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); IGRAPH_CHECK(igraph_dqueue_push(&q, actdist+1)); vdata[neighbor].parent = actnode; vdata[neighbor].level = actdist+1; } } /* Step 2: postorder tree traversal, determines the appropriate X * offsets for every node */ igraph_i_layout_reingold_tilford_postorder(vdata, root, no_of_nodes); /* Step 3: calculate real coordinates based on X offsets */ igraph_i_layout_reingold_tilford_calc_coords(vdata, res, root, no_of_nodes, vdata[root].offset); igraph_dqueue_destroy(&q); igraph_i_adjlist_destroy(&allneis); igraph_free(vdata); IGRAPH_FINALLY_CLEAN(3); igraph_progress("Reingold-Tilford tree layout", 100.0, NULL); return 0; } int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, igraph_matrix_t *res, long int node, long int vcount, igraph_real_t xpos) { long int i, n; MATRIX(*res, node, 0) = xpos; for (i=0, n=0; i= 0) { /* Now we will follow the right contour of leftroot and the * left contour of the subtree rooted at i */ long lnode, rnode; igraph_real_t loffset, roffset, minsep, rootsep; lnode = leftroot; rnode = i; minsep = 1; rootsep = vdata[leftroot].offset + minsep; loffset = 0; roffset = minsep; /* printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep);*/ while ((lnode >= 0) && (rnode >= 0)) { /* Step to the next level on the left contour */ if (vdata[lnode].right_contour >= 0) { lnode = vdata[lnode].right_contour; loffset += vdata[lnode].offset; } else { lnode = vdata[lnode].left_contour; if (lnode >= 0) loffset += vdata[lnode].offset; } /* Step to the next level on the right contour */ if (vdata[rnode].left_contour >= 0) { rnode = vdata[rnode].left_contour; roffset += vdata[rnode].offset; } else if (vdata[rnode].right_contour >= 0) { rnode = vdata[rnode].right_contour; roffset += vdata[rnode].offset; } else { /* Right subtree ended. If the left subtree is deeper, the * left contour will continue on the left subtree. We can * use only lnode here, since it has already been advanced * to the next level in the previous if statement */ vdata[rnode].left_contour = lnode; vdata[rnode].right_contour = lnode; rnode = -1; /* printf(" Right subtree ended, continuing its contours to %d\n", vdata[rnode].left_contour); */ } /* printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep);*/ /* Push subtrees away if necessary */ if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) { /* printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset); */ rootsep += minsep-roffset+loffset; roffset = loffset+minsep; } } /* printf(" Offset of subtree with root node %d will be %lf\n", i, rootsep); */ vdata[i].offset = rootsep; vdata[node].right_contour=i; avg = (avg*j)/(j+1) + rootsep/(j+1); leftrootidx=j; leftroot=i; } else { leftrootidx=j; leftroot=i; vdata[node].left_contour=i; avg = vdata[i].offset; } j++; } } /* printf("Shifting node to be centered above children. Shift amount: %lf\n", avg); */ vdata[node].offset += avg; for (i=0, j=0; i * First each layout is covered by a circle. Then the layout of the * largest graph is placed at the origin. Then the other layouts are * placed by the DLA algorithm, larger ones first and smaller ones * last. * \param thegraphs Pointer vector containing the graph object of * which the layouts will be merged. * \param coords Pointer vector containing matrix objects with the 2d * layouts of the graphs in \p thegraphs. * \param res Pointer to an initialized matrix object, the result will * be stored here. It will be resized if needed. * \return Error code. * * Added in version 0.2. This function is experimental. * * * Time complexity: TODO. */ int igraph_layout_merge_dla(igraph_vector_ptr_t *thegraphs, igraph_vector_ptr_t *coords, igraph_matrix_t *res) { long int graphs=igraph_vector_ptr_size(coords); igraph_vector_t sizes; igraph_vector_t x, y, r; igraph_vector_t nx, ny, nr; long int allnodes=0; long int i, j; long int actg; igraph_i_layout_mergegrid_t grid; long int jpos=0; igraph_real_t minx, maxx, miny, maxy; igraph_real_t area=0; igraph_real_t maxr=0; long int respos; IGRAPH_VECTOR_INIT_FINALLY(&sizes, graphs); IGRAPH_VECTOR_INIT_FINALLY(&x, graphs); IGRAPH_VECTOR_INIT_FINALLY(&y, graphs); IGRAPH_VECTOR_INIT_FINALLY(&r, graphs); IGRAPH_VECTOR_INIT_FINALLY(&nx, graphs); IGRAPH_VECTOR_INIT_FINALLY(&ny, graphs); IGRAPH_VECTOR_INIT_FINALLY(&nr, graphs); for (i=0; i maxr) { maxr=VECTOR(r)[i]; } igraph_i_layout_sphere_2d(mat, igraph_vector_e_ptr(&nx, i), igraph_vector_e_ptr(&ny, i), igraph_vector_e_ptr(&nr, i)); } igraph_vector_order2(&sizes); /* largest first */ /* 0. create grid */ minx=miny=-sqrt(5*area); maxx=maxy=sqrt(5*area); igraph_i_layout_mergegrid_init(&grid, minx, maxx, 200, miny, maxy, 200); IGRAPH_FINALLY(igraph_i_layout_mergegrid_destroy, &grid); /* fprintf(stderr, "Ok, starting DLA\n"); */ /* 1. place the largest */ actg=VECTOR(sizes)[jpos++]; igraph_i_layout_merge_place_sphere(&grid, 0, 0, VECTOR(r)[actg], actg); igraph_progress("Merging layouts via DLA", 0.0, NULL); while (jposxmax) { xmax=MATRIX(*coords,i,0); } if (MATRIX(*coords,i,1) < ymin) { ymin=MATRIX(*coords,i,1); } else if (MATRIX(*coords,i,1)>ymax) { ymax=MATRIX(*coords,i,1); } } *x=(xmin+xmax)/2; *y=(ymin+ymax)/2; *r=sqrt( (xmax-xmin)*(xmax-xmin)+(ymax-ymin)*(ymax-ymin) ) / 2; return 0; } int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *z, igraph_real_t *r) { long int nodes=igraph_matrix_nrow(coords); long int i; igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax; xmin=xmax=MATRIX(*coords,0,0); ymin=ymax=MATRIX(*coords,0,1); zmin=zmax=MATRIX(*coords,0,2); for (i=1; ixmax) { xmax=MATRIX(*coords,i,0); } if (MATRIX(*coords,i,1) < ymin) { ymin=MATRIX(*coords,i,1); } else if (MATRIX(*coords,i,1)>ymax) { ymax=MATRIX(*coords,i,1); } if (MATRIX(*coords,i,2) < zmin) { zmin=MATRIX(*coords,i,2); } else if (MATRIX(*coords,i,2)>zmax) { zmax=MATRIX(*coords,i,2); } } *x=(xmin+xmax)/2; *y=(ymin+ymax)/2; *z=(zmin+zmax)/2; *r=sqrt( (xmax-xmin)*(xmax-xmin)+(ymax-ymin)*(ymax-ymin)+ (zmax-zmin)*(zmax-zmin) ) / 2; return 0; } #define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2))) int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, long int actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, igraph_real_t cx, igraph_real_t cy, igraph_real_t startr, igraph_real_t killr) { long int sp=-1; igraph_real_t angle, len; long int steps=0; while (sp < 0) { /* start particle */ do { steps++; angle=RNG_UNIF(0,2*M_PI); len=RNG_UNIF(.5*startr, startr); *x=cx+len*cos(angle); *y=cy+len*sin(angle); sp=igraph_i_layout_mergegrid_get_sphere(grid, *x, *y, r); } while (sp >= 0); while (sp < 0 && DIST(*x,*y)