/* call-seq: * graph.layout_sphere -> IGraphMatrix * * Places vertices (more or less) uniformly on a sphere. * * The algorithm was described in the following paper: Distributing many * points on a sphere by E.B. Saff and A.B.J. Kuijlaars, Mathematical * Intelligencer 19.1 (1997) 5--11. */ VALUE cIGraph_layout_sphere(VALUE self) { igraph_t *graph; igraph_matrix_t *res = malloc(sizeof(igraph_matrix_t)); Data_Get_Struct(self, igraph_t, graph); igraph_matrix_init(res,0,0); igraph_layout_sphere(graph,res); return Data_Wrap_Struct(cIGraphMatrix, 0, cIGraph_matrix_free, res); }
int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_integer_t maxiter, igraph_real_t epsilon, igraph_real_t kkconst, const igraph_vector_t *weights, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz) { igraph_integer_t no_nodes=igraph_vcount(graph); igraph_integer_t no_edges=igraph_ecount(graph); igraph_real_t L, L0=sqrt(no_nodes); igraph_matrix_t dij, lij, kij; igraph_real_t max_dij; igraph_vector_t D1, D2, D3; igraph_integer_t i, j, m; if (maxiter < 0) { IGRAPH_ERROR("Number of iterations must be non-negatice in " "Kamada-Kawai layout", IGRAPH_EINVAL); } if (kkconst <= 0) { IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout", IGRAPH_EINVAL); } if (use_seed && (igraph_matrix_nrow(res) != no_nodes || igraph_matrix_ncol(res) != 3)) { IGRAPH_ERROR("Invalid start position matrix size in " "3d Kamada-Kawai layout", IGRAPH_EINVAL); } if (weights && igraph_vector_size(weights) != no_edges) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (minx && igraph_vector_size(minx) != no_nodes) { IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_nodes) { IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); } if (minx && maxx && !igraph_vector_all_le(minx, maxx)) { IGRAPH_ERROR("minx must not be greater than maxx", IGRAPH_EINVAL); } if (miny && igraph_vector_size(miny) != no_nodes) { IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_nodes) { IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); } if (miny && maxy && !igraph_vector_all_le(miny, maxy)) { IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } if (minz && igraph_vector_size(minz) != no_nodes) { IGRAPH_ERROR("Invalid minz vector length", IGRAPH_EINVAL); } if (maxz && igraph_vector_size(maxz) != no_nodes) { IGRAPH_ERROR("Invalid maxz vector length", IGRAPH_EINVAL); } if (minz && maxz && !igraph_vector_all_le(minz, maxz)) { IGRAPH_ERROR("minz must not be greater than maxz", IGRAPH_EINVAL); } if (!use_seed) { if (minx || maxx || miny || maxy || minz || maxz) { const igraph_real_t width=sqrt(no_nodes), height=width, depth=width; IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 3)); RNG_BEGIN(); for (i=0; i<no_nodes; i++) { igraph_real_t x1=minx ? VECTOR(*minx)[i] : -width/2; igraph_real_t x2=maxx ? VECTOR(*maxx)[i] : width/2; igraph_real_t y1=miny ? VECTOR(*miny)[i] : -height/2; igraph_real_t y2=maxy ? VECTOR(*maxy)[i] : height/2; igraph_real_t z1=minz ? VECTOR(*minz)[i] : -depth/2; igraph_real_t z2=maxz ? VECTOR(*maxz)[i] : depth/2; if (!igraph_finite(x1)) { x1 = -width/2; } if (!igraph_finite(x2)) { x2 = width/2; } if (!igraph_finite(y1)) { y1 = -height/2; } if (!igraph_finite(y2)) { y2 = height/2; } if (!igraph_finite(z1)) { z1 = -depth/2; } if (!igraph_finite(z2)) { z2 = depth/2; } MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); MATRIX(*res, i, 1) = RNG_UNIF(y1, y2); MATRIX(*res, i, 2) = RNG_UNIF(z1, z2); } RNG_END(); } else { igraph_layout_sphere(graph, res); } } if (no_nodes <= 1) { return 0; } IGRAPH_MATRIX_INIT_FINALLY(&dij, no_nodes, no_nodes); IGRAPH_MATRIX_INIT_FINALLY(&kij, no_nodes, no_nodes); IGRAPH_MATRIX_INIT_FINALLY(&lij, no_nodes, no_nodes); IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(), igraph_vss_all(), weights, IGRAPH_ALL)); max_dij = 0.0; for (i=0; i<no_nodes; i++) { for (j=i+1; j<no_nodes; j++) { if (!igraph_finite(MATRIX(dij, i, j))) { continue; } if (MATRIX(dij, i, j) > max_dij) { max_dij = MATRIX(dij, i, j); } } } for (i=0; i<no_nodes; i++) { for (j=0; j<no_nodes; j++) { if (MATRIX(dij, i, j) > max_dij) { MATRIX(dij, i, j) = max_dij; } } } L = L0 / max_dij; for (i=0; i<no_nodes; i++) { for (j=0; j<no_nodes; j++) { igraph_real_t tmp=MATRIX(dij, i, j) * MATRIX(dij, i, j); if (i==j) { continue; } MATRIX(kij, i, j) = kkconst / tmp; MATRIX(lij, i, j) = L * MATRIX(dij, i, j); } } /* Initialize delta */ IGRAPH_VECTOR_INIT_FINALLY(&D1, no_nodes); IGRAPH_VECTOR_INIT_FINALLY(&D2, no_nodes); IGRAPH_VECTOR_INIT_FINALLY(&D3, no_nodes); for (m=0; m<no_nodes; m++) { igraph_real_t myD1=0.0, myD2=0.0, myD3=0.0; for (i=0; i<no_nodes; i++) { if (i==m) { continue; } igraph_real_t dx=MATRIX(*res, m, 0) - MATRIX(*res, i, 0); igraph_real_t dy=MATRIX(*res, m, 1) - MATRIX(*res, i, 1); igraph_real_t dz=MATRIX(*res, m, 2) - MATRIX(*res, i, 2); igraph_real_t mi_dist=sqrt(dx * dx + dy * dy + dz * dz); myD1 += MATRIX(kij, m, i) * (dx - MATRIX(lij, m, i) * dx / mi_dist); myD2 += MATRIX(kij, m, i) * (dy - MATRIX(lij, m, i) * dy / mi_dist); myD3 += MATRIX(kij, m, i) * (dz - MATRIX(lij, m, i) * dz / mi_dist); } VECTOR(D1)[m] = myD1; VECTOR(D2)[m] = myD2; VECTOR(D3)[m] = myD3; } for (j=0; j<maxiter; j++) { igraph_real_t Ax=0.0, Ay=0.0, Az=0.0; igraph_real_t Axx=0.0, Axy=0.0, Axz=0.0, Ayy=0.0, Ayz=0.0, Azz=0.0; igraph_real_t max_delta, delta_x, delta_y, delta_z; igraph_real_t old_x, old_y, old_z, new_x, new_y, new_z; igraph_real_t detnum; /* Select maximal delta */ m=0; max_delta=-1; for (i=0; i<no_nodes; i++) { igraph_real_t delta=(VECTOR(D1)[i] * VECTOR(D1)[i] + VECTOR(D2)[i] * VECTOR(D2)[i] + VECTOR(D3)[i] * VECTOR(D3)[i]); if (delta > max_delta) { m=i; max_delta=delta; } } if (max_delta < epsilon) { break; } old_x=MATRIX(*res, m, 0); old_y=MATRIX(*res, m, 1); old_z=MATRIX(*res, m, 2); /* Calculate D1, D2 and D3, and other coefficients */ for (i=0; i<no_nodes; i++) { if (i==m) { continue; } igraph_real_t dx=old_x - MATRIX(*res, i, 0); igraph_real_t dy=old_y - MATRIX(*res, i, 1); igraph_real_t dz=old_z - MATRIX(*res, i, 2); igraph_real_t dist=sqrt(dx * dx + dy * dy + dz *dz); igraph_real_t den=dist * (dx * dx + dy * dy + dz * dz); igraph_real_t k_mi=MATRIX(kij, m, i); igraph_real_t l_mi=MATRIX(lij, m, i); Axx += k_mi * (1 - l_mi * (dy*dy + dz*dz) / den); Ayy += k_mi * (1 - l_mi * (dx*dx + dz*dz) / den); Azz += k_mi * (1 - l_mi * (dx*dx + dy*dy) / den); Axy += k_mi * l_mi * dx * dy / den; Axz += k_mi * l_mi * dx * dz / den; Ayz += k_mi * l_mi * dy * dz / den; } Ax = -VECTOR(D1)[m]; Ay = -VECTOR(D2)[m]; Az = -VECTOR(D3)[m]; /* Need to solve some linear equations, we just use Cramer's rule */ #define DET(a,b,c,d,e,f,g,h,i) ((a*e*i+b*f*g+c*d*h)-(c*e*g+b*d*i+a*f*h)) detnum = DET(Axx,Axy,Axz, Axy,Ayy,Ayz, Axz,Ayz,Azz); delta_x = DET(Ax ,Ay ,Az , Axy,Ayy,Ayz, Axz,Ayz,Azz) / detnum; delta_y = DET(Axx,Axy,Axz, Ax ,Ay ,Az , Axz,Ayz,Azz) / detnum; delta_z = DET(Axx,Axy,Axz, Axy,Ayy,Ayz, Ax ,Ay ,Az ) / detnum; new_x = old_x + delta_x; new_y = old_y + delta_y; new_z = old_z + delta_z; /* Limits, if given */ if (minx && new_x < VECTOR(*minx)[m]) { new_x = VECTOR(*minx)[m]; } if (maxx && new_x > VECTOR(*maxx)[m]) { new_x = VECTOR(*maxx)[m]; } if (miny && new_y < VECTOR(*miny)[m]) { new_y = VECTOR(*miny)[m]; } if (maxy && new_y > VECTOR(*maxy)[m]) { new_y = VECTOR(*maxy)[m]; } if (minz && new_z < VECTOR(*minz)[m]) { new_z = VECTOR(*minz)[m]; } if (maxz && new_z > VECTOR(*maxz)[m]) { new_z = VECTOR(*maxz)[m]; } /* Update delta, only with/for the affected node */ VECTOR(D1)[m] = VECTOR(D2)[m] = VECTOR(D3)[m] = 0.0; for (i=0; i<no_nodes; i++) { if (i==m) { continue; } igraph_real_t old_dx=old_x - MATRIX(*res, i, 0); igraph_real_t old_dy=old_y - MATRIX(*res, i, 1); igraph_real_t old_dz=old_z - MATRIX(*res, i, 2); igraph_real_t old_mi_dist=sqrt(old_dx * old_dx + old_dy * old_dy + old_dz * old_dz); igraph_real_t new_dx=new_x - MATRIX(*res, i, 0); igraph_real_t new_dy=new_y - MATRIX(*res, i, 1); igraph_real_t new_dz=new_z - MATRIX(*res, i, 2); igraph_real_t new_mi_dist=sqrt(new_dx * new_dx + new_dy * new_dy + new_dz * new_dz); VECTOR(D1)[i] -= MATRIX(kij, m, i) * (-old_dx + MATRIX(lij, m, i) * old_dx / old_mi_dist); VECTOR(D2)[i] -= MATRIX(kij, m, i) * (-old_dy + MATRIX(lij, m, i) * old_dy / old_mi_dist); VECTOR(D3)[i] -= MATRIX(kij, m, i) * (-old_dz + MATRIX(lij, m, i) * old_dz / old_mi_dist); VECTOR(D1)[i] += MATRIX(kij, m, i) * (-new_dx + MATRIX(lij, m, i) * new_dx / new_mi_dist); VECTOR(D2)[i] += MATRIX(kij, m, i) * (-new_dy + MATRIX(lij, m, i) * new_dy / new_mi_dist); VECTOR(D3)[i] += MATRIX(kij, m, i) * (-new_dz + MATRIX(lij, m, i) * new_dz / new_mi_dist); VECTOR(D1)[m] += MATRIX(kij, m, i) * (new_dx - MATRIX(lij, m, i) * new_dx / new_mi_dist); VECTOR(D2)[m] += MATRIX(kij, m, i) * (new_dy - MATRIX(lij, m, i) * new_dy / new_mi_dist); VECTOR(D3)[m] += MATRIX(kij, m, i) * (new_dz - MATRIX(lij, m, i) * new_dz / new_mi_dist); } /* Update coordinates*/ MATRIX(*res, m, 0) = new_x; MATRIX(*res, m, 1) = new_y; MATRIX(*res, m, 2) = new_z; } igraph_vector_destroy(&D3); igraph_vector_destroy(&D2); igraph_vector_destroy(&D1); igraph_matrix_destroy(&lij); igraph_matrix_destroy(&kij); igraph_matrix_destroy(&dij); IGRAPH_FINALLY_CLEAN(6); return 0; }