1 2## ---------------------------------------------------------------- 3## 4## IGraph R package 5## Copyright (C) 2003-2014 Gabor Csardi <csardi.gabor@gmail.com> 6## 334 Harvard street, Cambridge, MA 02139 USA 7## 8## This program is free software; you can redistribute it and/or modify 9## it under the terms of the GNU General Public License as published by 10## the Free Software Foundation; either version 2 of the License, or 11## (at your option) any later version. 12## 13## This program is distributed in the hope that it will be useful, 14## but WITHOUT ANY WARRANTY; without even the implied warranty of 15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16## GNU General Public License for more details. 17## 18## You should have received a copy of the GNU General Public License 19## along with this program; if not, write to the Free Software 20## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21## 02110-1301 USA 22## 23## ---------------------------------------------------------------- 24 25 26## ---------------------------------------------------------------- 27## This is the new layout API 28## ---------------------------------------------------------------- 29 30 31#' Graph layouts 32#' 33#' This is a generic function to apply a layout function to 34#' a graph. 35#' 36#' There are two ways to calculate graph layouts in igraph. 37#' The first way is to call a layout function (they all have 38#' prefix \code{layout_} on a graph, to get the vertex coordinates. 39#' 40#' The second way (new in igraph 0.8.0), has two steps, and it 41#' is more flexible. First you call a layout specification 42#' function (the one without the \code{layout_} prefix, and 43#' then \code{layout_} (or \code{\link{add_layout_}}) to 44#' perform the layouting. 45#' 46#' The second way is preferred, as it is more flexible. It allows 47#' operations before and after the layouting. E.g. using the 48#' \code{component_wise} argument, the layout can be calculated 49#' separately for each component, and then merged to get the 50#' final results. 51#' 52#' @aliases layout 53#' @section Modifiers: 54#' Modifiers modify how a layout calculation is performed. 55#' Currently implemented modifiers: \itemize{ 56#' \item \code{component_wise} calculates the layout separately 57#' for each component of the graph, and then merges 58#' them. 59#' \item \code{normalize} scales the layout to a square. 60#' } 61#' 62#' @param graph The input graph. 63#' @param layout The layout specification. It must be a call 64#' to a layout specification function. 65#' @param ... Further modifiers, see a complete list below. 66#' For the \code{print} methods, it is ignored. 67#' @return The return value of the layout function, usually a 68#' two column matrix. For 3D layouts a three column matrix. 69#' 70#' @seealso \code{\link{add_layout_}} to add the layout to the 71#' graph as an attribute. 72#' @export 73#' @family graph layouts 74#' @examples 75#' g <- make_ring(10) + make_full_graph(5) 76#' coords <- layout_(g, as_star()) 77#' plot(g, layout = coords) 78 79layout_ <- function(graph, layout, ...) { 80 81 modifiers <- list(...) 82 stopifnot(all(sapply(modifiers, inherits, 83 what = "igraph_layout_modifier"))) 84 85 ids <- sapply(modifiers, "[[", "id") 86 stopifnot(all(ids %in% c("component_wise", "normalize"))) 87 if (anyDuplicated(ids)) stop("Duplicate modifiers") 88 names(modifiers) <- ids 89 90 ## TODO: better, generic mechanism for modifiers 91 if ("component_wise" %in% ids) { 92 graph$id <- seq(vcount(graph)) 93 comps <- decompose(graph) 94 coords <- lapply(comps, function(comp) { 95 do_call(layout$fun, list(graph = comp), layout$args) 96 }) 97 all_coords <- merge_coords( 98 comps, 99 coords, 100 method = modifiers[["component_wise"]]$args$merge_method 101 ) 102 all_coords[ unlist(sapply(comps, vertex_attr, "id")), ] <- all_coords[] 103 result <- all_coords 104 105 } else { 106 result <- do_call(layout$fun, list(graph = graph), layout$args) 107 } 108 109 if ("normalize" %in% ids) { 110 result <- do_call(norm_coords, list(result), 111 modifiers[["normalize"]]$args) 112 } 113 114 result 115} 116 117 118#' Add layout to graph 119#' 120#' @param graph The input graph. 121#' @param ... Additional arguments are passed to \code{\link{layout_}}. 122#' @param overwrite Whether to overwrite the layout of the graph, 123#' if it already has one. 124#' @return The input graph, with the layout added. 125#' 126#' @seealso \code{\link{layout_}} for a description of the layout API. 127#' @export 128#' @family graph layouts 129#' @examples 130#' (make_star(11) + make_star(11)) %>% 131#' add_layout_(as_star(), component_wise()) %>% 132#' plot() 133 134add_layout_ <- function(graph, ..., overwrite = TRUE) { 135 if (overwrite && 'layout' %in% graph_attr_names(graph)) { 136 graph <- delete_graph_attr(graph, 'layout') 137 } 138 graph$layout <- layout_(graph, ...) 139 graph 140} 141 142 143layout_spec <- function(fun, ...) { 144 my_call <- match.call(sys.function(1), sys.call(1)) 145 my_call[[1]] <- substitute(fun) 146 structure( 147 list( 148 fun = fun, 149 call_str = sub("(", "(<graph>, ", deparse(my_call), fixed = TRUE), 150 args = list(...) 151 ), 152 class = "igraph_layout_spec" 153 ) 154} 155 156 157#' @rdname layout_ 158#' @param x The layout specification 159#' @method print igraph_layout_spec 160#' @export 161 162print.igraph_layout_spec <- function(x, ...) { 163 cat(paste( 164 sep = "", 165 "igraph layout specification, see ?layout_:\n", 166 x$call_str, "\n" 167 )) 168} 169 170 171layout_modifier <- function(...) { 172 structure( 173 list(...), 174 class = "igraph_layout_modifier" 175 ) 176} 177 178 179#' @rdname layout_ 180#' @method print igraph_layout_modifier 181#' @export 182 183print.igraph_layout_modifier <- function(x, ...) { 184 cat(sep = "", "igraph layout modifier: ", x$id, ".\n") 185} 186 187#' Component-wise layout 188#' 189#' This is a layout modifier function, and it can be used 190#' to calculate the layout separately for each component 191#' of the graph. 192#' 193#' @param merge_method Merging algorithm, the \code{method} 194#' argument of \code{\link{merge_coords}}. 195#' 196#' @family layout modifiers 197#' @family graph layouts 198#' @seealso \code{\link{merge_coords}}, \code{\link{layout_}}. 199#' @export 200#' @examples 201#' g <- make_ring(10) + make_ring(10) 202#' g %>% 203#' add_layout_(in_circle(), component_wise()) %>% 204#' plot() 205 206component_wise <- function(merge_method = "dla") { 207 208 args <- grab_args() 209 210 layout_modifier( 211 id = "component_wise", 212 args = args 213 ) 214} 215 216#' Normalize layout 217#' 218#' Scale coordinates of a layout. 219#' 220#' @param xmin,xmax Minimum and maximum for x coordinates. 221#' @param ymin,ymax Minimum and maximum for y coordinates. 222#' @param zmin,zmax Minimum and maximum for z coordinates. 223#' 224#' @family layout modifiers 225#' @family graph layouts 226#' @seealso \code{\link{merge_coords}}, \code{\link{layout_}}. 227#' @export 228#' @examples 229#' layout_(make_ring(10), with_fr(), normalize()) 230 231normalize <- function(xmin = -1, xmax = 1, ymin = xmin, ymax = xmax, 232 zmin = xmin, zmax = xmax) { 233 234 args <- grab_args() 235 236 layout_modifier( 237 id = "normalize", 238 args = args 239 ) 240} 241 242## ---------------------------------------------------------------- 243## Layout definitions for the new API 244## ---------------------------------------------------------------- 245 246 247#' Simple two-row layout for bipartite graphs 248#' 249#' Minimize edge-crossings in a simple two-row (or column) layout for bipartite 250#' graphs. 251#' 252#' The layout is created by first placing the vertices in two rows, according 253#' to their types. Then the positions within the rows are optimized to minimize 254#' edge crossings, using the Sugiyama algorithm (see 255#' \code{\link{layout_with_sugiyama}}). 256#' 257#' @aliases layout_as_bipartite layout.bipartite 258#' @param graph The bipartite input graph. It should have a logical 259#' \sQuote{\code{type}} vertex attribute, or the \code{types} argument must be 260#' given. 261#' @param types A logical vector, the vertex types. If this argument is 262#' \code{NULL} (the default), then the \sQuote{\code{type}} vertex attribute is 263#' used. 264#' @param hgap Real scalar, the minimum horizontal gap between vertices in the 265#' same layer. 266#' @param vgap Real scalar, the distance between the two layers. 267#' @param maxiter Integer scalar, the maximum number of iterations in the 268#' crossing minimization stage. 100 is a reasonable default; if you feel that 269#' you have too many edge crossings, increase this. 270#' @return A matrix with two columns and as many rows as the number of vertices 271#' in the input graph. 272#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 273#' @seealso \code{\link{layout_with_sugiyama}} 274#' @keywords graphs 275#' @export 276#' @family graph layouts 277#' @examples 278#' # Random bipartite graph 279#' inc <- matrix(sample(0:1, 50, replace = TRUE, prob=c(2,1)), 10, 5) 280#' g <- graph_from_incidence_matrix(inc) 281#' plot(g, layout = layout_as_bipartite, 282#' vertex.color=c("green","cyan")[V(g)$type+1]) 283#' 284#' # Two columns 285#' g %>% 286#' add_layout_(as_bipartite()) %>% 287#' plot() 288 289layout_as_bipartite <- function(graph, types = NULL, hgap = 1, vgap = 1, 290 maxiter = 100) { 291 292 ## Argument checks 293 if (!is_igraph(graph)) { stop("Not a graph object") } 294 types <- handle_vertex_type_arg(types, graph) 295 hgap <- as.numeric(hgap) 296 vgap <- as.numeric(vgap) 297 maxiter <- as.integer(maxiter) 298 299 on.exit(.Call(C_R_igraph_finalizer) ) 300 301 ## Function call 302 res <- .Call(C_R_igraph_layout_bipartite, graph, types, hgap, vgap, maxiter) 303 304 res 305} 306 307 308#' @rdname layout_as_bipartite 309#' @param ... Arguments to pass to \code{layout_as_bipartite}. 310#' @export 311 312as_bipartite <- function(...) layout_spec(layout_as_bipartite, ...) 313 314 315## ---------------------------------------------------------------- 316 317 318#' Generate coordinates to place the vertices of a graph in a star-shape 319#' 320#' A simple layout generator, that places one vertex in the center of a circle 321#' and the rest of the vertices equidistantly on the perimeter. 322#' 323#' It is possible to choose the vertex that will be in the center, and the 324#' order of the vertices can be also given. 325#' 326#' @aliases layout_as_star layout.star 327#' @param graph The graph to layout. 328#' @param center The id of the vertex to put in the center. By default it is 329#' the first vertex. 330#' @param order Numeric vector, the order of the vertices along the perimeter. 331#' The default ordering is given by the vertex ids. 332#' @return A matrix with two columns and as many rows as the number of vertices 333#' in the input graph. 334#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 335#' @seealso \code{\link{layout}} and \code{\link{layout.drl}} for other layout 336#' algorithms, \code{\link{plot.igraph}} and \code{\link{tkplot}} on how to 337#' plot graphs and \code{\link{star}} on how to create ring graphs. 338#' @keywords graphs 339#' @export 340#' @family graph layouts 341#' @examples 342#' 343#' g <- make_star(10) 344#' layout_as_star(g) 345#' 346#' ## Alternative form 347#' layout_(g, as_star()) 348 349layout_as_star <- function(graph, center=V(graph)[1], order=NULL) { 350 # Argument checks 351 if (!is_igraph(graph)) { stop("Not a graph object") } 352 center <- as.igraph.vs(graph, center) 353 if (!is.null(order)) order <- as.numeric(order)-1 354 355 on.exit(.Call(C_R_igraph_finalizer) ) 356 # Function call 357 res <- .Call(C_R_igraph_layout_star, graph, center-1, order) 358 359 res 360} 361 362 363#' @rdname layout_as_star 364#' @param ... Arguments to pass to \code{layout_as_star}. 365#' @export 366 367as_star <- function(...) layout_spec(layout_as_star, ...) 368 369 370## ---------------------------------------------------------------- 371 372 373#' The Reingold-Tilford graph layout algorithm 374#' 375#' A tree-like layout, it is perfect for trees, acceptable for graphs with not 376#' too many cycles. 377#' 378#' Arranges the nodes in a tree where the given node is used as the root. The 379#' tree is directed downwards and the parents are centered above its children. 380#' For the exact algorithm, the reference below. 381#' 382#' If the given graph is not a tree, a breadth-first search is executed first 383#' to obtain a possible spanning tree. 384#' 385#' @param graph The input graph. 386#' @param root The index of the root vertex or root vertices. If this is a 387#' non-empty vector then the supplied vertex ids are used as the roots of the 388#' trees (or a single tree if the graph is connected). If it is an empty 389#' vector, then the root vertices are automatically calculated based on 390#' topological sorting, performed with the opposite mode than the \code{mode} 391#' argument. After the vertices have been sorted, one is selected from each 392#' component. 393#' @param circular Logical scalar, whether to plot the tree in a circular 394#' fashion. Defaults to \code{FALSE}, so the tree branches are going bottom-up 395#' (or top-down, see the \code{flip.y} argument. 396#' @param rootlevel This argument can be useful when drawing forests which are 397#' not trees (i.e. they are unconnected and have tree components). It specifies 398#' the level of the root vertices for every tree in the forest. It is only 399#' considered if the \code{roots} argument is not an empty vector. 400#' @param mode Specifies which edges to consider when building the tree. If it 401#' is \sQuote{out}, then only the outgoing, if it is \sQuote{in}, then only the 402#' incoming edges of a parent are considered. If it is \sQuote{all} then all 403#' edges are used (this was the behavior in igraph 0.5 and before). This 404#' parameter also influences how the root vertices are calculated, if they are 405#' not given. See the \code{roots} parameter. 406#' @param flip.y Logical scalar, whether to flip the \sQuote{y} coordinates. 407#' The default is flipping because that puts the root vertex on the top. 408#' @return A numeric matrix with two columns, and one row for each vertex. 409#' @author Tamas Nepusz \email{ntamas@@gmail.com} and Gabor Csardi 410#' \email{csardi.gabor@@gmail.com} 411#' @references Reingold, E and Tilford, J (1981). Tidier drawing of trees. 412#' \emph{IEEE Trans. on Softw. Eng.}, SE-7(2):223--228. 413#' @keywords graphs 414#' @export 415#' @family graph layouts 416#' @examples 417#' 418#' tree <- make_tree(20, 3) 419#' plot(tree, layout=layout_as_tree) 420#' plot(tree, layout=layout_as_tree(tree, flip.y=FALSE)) 421#' plot(tree, layout=layout_as_tree(tree, circular=TRUE)) 422#' 423#' tree2 <- make_tree(10, 3) + make_tree(10, 2) 424#' plot(tree2, layout=layout_as_tree) 425#' plot(tree2, layout=layout_as_tree(tree2, root=c(1,11), 426#' rootlevel=c(2,1))) 427 428layout_as_tree <- function(graph, root=numeric(), circular=FALSE, 429 rootlevel=numeric(), mode=c("out", "in", "all"), 430 flip.y=TRUE) { 431 432 if (!is_igraph(graph)) { 433 stop("Not a graph object") 434 } 435 root <- as.igraph.vs(graph, root)-1 436 circular <- as.logical(circular) 437 rootlevel <- as.double(rootlevel) 438 mode <- switch(igraph.match.arg(mode), "out"=1, "in"=2, "all"=3, 439 "total"=3) 440 flip.y <- as.logical(flip.y) 441 442 on.exit(.Call(C_R_igraph_finalizer) ) 443 res <- .Call(C_R_igraph_layout_reingold_tilford, graph, root, mode, 444 rootlevel, circular) 445 if (flip.y) { res[,2] <- max(res[,2])-res[,2] } 446 res 447} 448 449 450#' @rdname layout_as_tree 451#' @param ... Passed to \code{layout_as_tree}. 452#' @export 453 454as_tree <- function(...) layout_spec(layout_as_tree, ...) 455 456#' @export 457#' @rdname layout.deprecated 458 459layout.reingold.tilford <- function(..., params = list()) { 460 do_call(layout_as_tree, .args = c(list(...), params)) 461} 462 463## ---------------------------------------------------------------- 464 465 466#' Graph layout with vertices on a circle. 467#' 468#' Place vertices on a circle, in the order of their vertex ids. 469#' 470#' If you want to order the vertices differently, then permute them using the 471#' \code{\link{permute}} function. 472#' 473#' @param graph The input graph. 474#' @param order The vertices to place on the circle, in the order of their 475#' desired placement. Vertices that are not included here will be placed at 476#' (0,0). 477#' @return A numeric matrix with two columns, and one row for each vertex. 478#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 479#' @keywords graphs 480#' @export 481#' @family graph layouts 482#' @examples 483#' 484#' ## Place vertices on a circle, order them according to their 485#' ## community 486#' \dontrun{ 487#' library(igraphdata) 488#' data(karate) 489#' karate_groups <- cluster_optimal(karate) 490#' coords <- layout_in_circle(karate, order = 491#' order(membership(karate_groups))) 492#' V(karate)$label <- sub("Actor ", "", V(karate)$name) 493#' V(karate)$label.color <- membership(karate_groups) 494#' V(karate)$shape <- "none" 495#' plot(karate, layout = coords) 496#' } 497 498layout_in_circle <- function(graph, order=V(graph)) { 499 if (!is_igraph(graph)) { 500 stop("Not a graph object") 501 } 502 order <- as.igraph.vs(graph, order) - 1L 503 on.exit(.Call(C_R_igraph_finalizer) ) 504 .Call(C_R_igraph_layout_circle, graph, order) 505} 506 507#' @rdname layout_in_circle 508#' @param ... Passed to \code{layout_in_circle}. 509#' @export 510 511in_circle <- function(...) layout_spec(layout_in_circle, ...) 512 513#' @export 514#' @rdname layout.deprecated 515 516layout.circle <- function(..., params = list()) { 517 do_call(layout_in_circle, .args = c(list(...), params)) 518} 519 520## ---------------------------------------------------------------- 521 522 523#' Choose an appropriate graph layout algorithm automatically 524#' 525#' This function tries to choose an appropriate graph layout algorithm for the 526#' graph, automatically, based on a simple algorithm. See details below. 527#' 528#' \code{layout_nicely} tries to choose an appropriate layout function for the 529#' supplied graph, and uses that to generate the layout. The current 530#' implementation works like this: \enumerate{ \item If the graph has a graph 531#' attribute called \sQuote{layout}, then this is used. If this attribute is an 532#' R function, then it is called, with the graph and any other extra arguments. 533#' \item Otherwise, if the graph has vertex attributes called \sQuote{x} and 534#' \sQuote{y}, then these are used as coordinates. If the graph has an 535#' additional \sQuote{z} vertex attribute, that is also used. \item Otherwise, 536#' if the graph is connected and has less than 1000 vertices, the 537#' Fruchterman-Reingold layout is used, by calling \code{layout_with_fr}. 538#' \item Otherwise the DrL layout is used, \code{layout_with_drl} is called. } 539#' 540#' @aliases layout.auto 541#' @param graph The input graph 542#' @param dim Dimensions, should be 2 or 3. 543#' @param \dots For \code{layout_nicely} the extra arguments are passed to 544#' the real layout function. For \code{nicely} all argument are passed to 545#' \code{layout_nicely}. 546#' @return A numeric matrix with two or three columns. 547#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 548#' @seealso \code{\link{plot.igraph}} 549#' @keywords graphs 550#' @export 551#' @family graph layouts 552 553layout_nicely <- function(graph, dim=2, ...) { 554 555 ## 1. If there is a 'layout' graph attribute, we just use that. 556 ## 2. Otherwise, if there are vertex attributes called 'x' and 'y', 557 ## we use those (and the 'z' vertex attribute as well, if present). 558 ## 3. Otherwise, if the graph is small (<1000) we use 559 ## the Fruchterman-Reingold layout. 560 ## 5. Otherwise we use the DrL layout generator. 561 562 if ("layout" %in% graph_attr_names(graph)) { 563 lay <- graph_attr(graph, "layout") 564 if (is.function(lay)) { 565 lay(graph, ...) 566 } else { 567 lay 568 } 569 570 } else if ( all(c("x", "y") %in% vertex_attr_names(graph)) ) { 571 if ("z" %in% vertex_attr_names(graph)) { 572 cbind(V(graph)$x, V(graph)$y, V(graph)$z) 573 } else { 574 cbind(V(graph)$x, V(graph)$y) 575 } 576 577 } else if (vcount(graph) < 1000) { 578 layout_with_fr(graph, dim=dim, ...) 579 580 } else { 581 layout_with_drl(graph, dim=dim, ...) 582 } 583 584} 585 586 587#' @rdname layout_nicely 588#' @export 589 590nicely <- function(...) layout_spec(layout_nicely, ...) 591 592 593## ---------------------------------------------------------------- 594 595 596#' Simple grid layout 597#' 598#' This layout places vertices on a rectangular grid, in two or three 599#' dimensions. 600#' 601#' The function places the vertices on a simple rectangular grid, one after the 602#' other. If you want to change the order of the vertices, then see the 603#' \code{\link{permute}} function. 604#' 605#' @aliases layout_on_grid layout.grid layout.grid.3d 606#' @param graph The input graph. 607#' @param width The number of vertices in a single row of the grid. If this is 608#' zero or negative, then for 2d layouts the width of the grid will be the 609#' square root of the number of vertices in the graph, rounded up to the next 610#' integer. Similarly, it will be the cube root for 3d layouts. 611#' @param height The number of vertices in a single column of the grid, for 612#' three dimensional layouts. If this is zero or negative, then it is 613#' determinted automatically. 614#' @param dim Two or three. Whether to make 2d or a 3d layout. 615#' @return A two-column or three-column matrix. 616#' @author Tamas Nepusz \email{ntamas@@gmail.com} 617#' @seealso \code{\link{layout}} for other layout generators 618#' @keywords graphs 619#' @export 620#' @family graph layouts 621#' @examples 622#' 623#' g <- make_lattice( c(3,3) ) 624#' layout_on_grid(g) 625#' 626#' g2 <- make_lattice( c(3,3,3) ) 627#' layout_on_grid(g2, dim = 3) 628#' 629#' \dontrun{ 630#' plot(g, layout=layout_on_grid) 631#' rglplot(g, layout=layout_on_grid(g, dim = 3)) 632#' } 633 634layout_on_grid <- function(graph, width = 0, height = 0, dim = 2) { 635 # Argument checks 636 if (!is_igraph(graph)) { stop("Not a graph object") } 637 width <- as.integer(width) 638 dim <- as.integer(dim) 639 stopifnot(dim == 2 || dim == 3) 640 if (dim == 3) { height <- as.integer(height) } 641 642 on.exit(.Call(C_R_igraph_finalizer) ) 643 # Function call 644 if (dim == 2) { 645 res <- .Call(C_R_igraph_layout_grid, graph, width) 646 } else { 647 res <- .Call(C_R_igraph_layout_grid_3d, graph, width, height) 648 } 649 650 res 651} 652 653 654#' @rdname layout_on_grid 655#' @param ... Passed to \code{layout_on_grid}. 656#' @export 657 658on_grid <- function(...) layout_spec(layout_on_grid, ...) 659 660 661#' @rdname layout_on_grid 662#' @export 663 664layout.grid.3d <- function(graph, width=0, height=0) { 665 .Deprecated("layout_on_grid", msg = paste0("layout.grid.3d is deprecated from\n", 666 "igraph 0.8.0, please use layout_on_grid instead")) 667 # Argument checks 668 if (!is_igraph(graph)) { stop("Not a graph object") } 669 width <- as.integer(width) 670 height <- as.integer(height) 671 672 on.exit(.Call(C_R_igraph_finalizer) ) 673 # Function call 674 res <- .Call(C_R_igraph_layout_grid_3d, graph, width, height) 675 676 res 677} 678 679## ---------------------------------------------------------------- 680 681 682#' Graph layout with vertices on the surface of a sphere 683#' 684#' Place vertices on a sphere, approximately uniformly, in the order of their 685#' vertex ids. 686#' 687#' \code{layout_on_sphere} places the vertices (approximately) uniformly on the 688#' surface of a sphere, this is thus a 3d layout. It is not clear however what 689#' \dQuote{uniformly on a sphere} means. 690#' 691#' If you want to order the vertices differently, then permute them using the 692#' \code{\link{permute}} function. 693#' 694#' @param graph The input graph. 695#' @return A numeric matrix with three columns, and one row for each vertex. 696#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 697#' @keywords graphs 698#' @export 699#' @family graph layouts 700 701layout_on_sphere <- function(graph) { 702 if (!is_igraph(graph)) { 703 stop("Not a graph object") 704 } 705 on.exit(.Call(C_R_igraph_finalizer) ) 706 .Call(C_R_igraph_layout_sphere, graph) 707} 708 709 710#' @rdname layout_on_sphere 711#' @param ... Passed to \code{layout_on_sphere}. 712#' @export 713 714on_sphere <- function(...) layout_spec(layout_on_sphere, ...) 715 716#' @export 717#' @rdname layout.deprecated 718 719layout.sphere <- function(..., params = list()) { 720 do_call(layout_on_sphere, .args = c(list(...), params)) 721} 722 723## ---------------------------------------------------------------- 724 725 726#' Randomly place vertices on a plane or in 3d space 727#' 728#' This function uniformly randomly places the vertices of the graph in two or 729#' three dimensions. 730#' 731#' Randomly places vertices on a [-1,1] square (in 2d) or in a cube (in 3d). It 732#' is probably a useless layout, but it can use as a starting point for other 733#' layout generators. 734#' 735#' @param graph The input graph. 736#' @param dim Integer scalar, the dimension of the space to use. It must be 2 737#' or 3. 738#' @return A numeric matrix with two or three columns. 739#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 740#' @keywords graphs 741#' @export 742#' @family graph layouts 743 744layout_randomly <- function(graph, dim=2) { 745 if (!is_igraph(graph)) { 746 stop("Not a graph object") 747 } 748 if (dim==2) { 749 on.exit(.Call(C_R_igraph_finalizer) ) 750 .Call(C_R_igraph_layout_random, graph) 751 } else if (dim==3) { 752 on.exit(.Call(C_R_igraph_finalizer) ) 753 .Call(C_R_igraph_layout_random_3d, graph) 754 } else { 755 stop("Invalid `dim' value"); 756 } 757} 758 759#' @rdname layout_randomly 760#' @param ... Parameters to pass to \code{layout_randomly}. 761#' @export 762 763randomly <- function(...) layout_spec(layout_randomly, ...) 764 765#' Deprecated layout functions 766#' 767#' Please use the new names, see \code{\link{layout_}}. 768#' 769#' @param ... Passed to the new layout functions. 770#' @param params Passed to the new layout functions as arguments. 771#' @export 772#' @rdname layout.deprecated 773 774layout.random <- function(..., params = list()) { 775 do_call(layout_randomly, .args = c(list(...), params)) 776} 777 778 779## ---------------------------------------------------------------- 780 781 782 783#' The Davidson-Harel layout algorithm 784#' 785#' Place vertices of a graph on the plane, according to the simulated annealing 786#' algorithm by Davidson and Harel. 787#' 788#' This function implements the algorithm by Davidson and Harel, see Ron 789#' Davidson, David Harel: Drawing Graphs Nicely Using Simulated Annealing. ACM 790#' Transactions on Graphics 15(4), pp. 301-331, 1996. 791#' 792#' The algorithm uses simulated annealing and a sophisticated energy function, 793#' which is unfortunately hard to parameterize for different graphs. The 794#' original publication did not disclose any parameter values, and the ones 795#' below were determined by experimentation. 796#' 797#' The algorithm consists of two phases, an annealing phase, and a fine-tuning 798#' phase. There is no simulated annealing in the second phase. 799#' 800#' Our implementation tries to follow the original publication, as much as 801#' possible. The only major difference is that coordinates are explicitly kept 802#' within the bounds of the rectangle of the layout. 803#' 804#' @aliases layout.davidson.harel 805#' @param graph The graph to lay out. Edge directions are ignored. 806#' @param coords Optional starting positions for the vertices. If this argument 807#' is not \code{NULL} then it should be an appropriate matrix of starting 808#' coordinates. 809#' @param maxiter Number of iterations to perform in the first phase. 810#' @param fineiter Number of iterations in the fine tuning phase. 811#' @param cool.fact Cooling factor. 812#' @param weight.node.dist Weight for the node-node distances component of the 813#' energy function. 814#' @param weight.border Weight for the distance from the border component of 815#' the energy function. It can be set to zero, if vertices are allowed to sit 816#' on the border. 817#' @param weight.edge.lengths Weight for the edge length component of the 818#' energy function. 819#' @param weight.edge.crossings Weight for the edge crossing component of the 820#' energy function. 821#' @param weight.node.edge.dist Weight for the node-edge distance component of 822#' the energy function. 823#' @return A two- or three-column matrix, each row giving the coordinates of a 824#' vertex, according to the ids of the vertex ids. 825#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 826#' @seealso \code{\link{layout_with_fr}}, 827#' \code{\link{layout_with_kk}} for other layout algorithms. 828#' @references Ron Davidson, David Harel: Drawing Graphs Nicely Using Simulated 829#' Annealing. \emph{ACM Transactions on Graphics} 15(4), pp. 301-331, 1996. 830#' @export 831#' @family graph layouts 832#' @examples 833#' 834#' set.seed(42) 835#' ## Figures from the paper 836#' g_1b <- make_star(19, mode="undirected") + path(c(2:19, 2)) + 837#' path(c(seq(2, 18, by=2), 2)) 838#' plot(g_1b, layout=layout_with_dh) 839#' 840#' g_2 <- make_lattice(c(8, 3)) + edges(1,8, 9,16, 17,24) 841#' plot(g_2, layout=layout_with_dh) 842#' 843#' g_3 <- make_empty_graph(n=70) 844#' plot(g_3, layout=layout_with_dh) 845#' 846#' g_4 <- make_empty_graph(n=70, directed=FALSE) + edges(1:70) 847#' plot(g_4, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 848#' 849#' g_5a <- make_ring(24) 850#' plot(g_5a, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 851#' 852#' g_5b <- make_ring(40) 853#' plot(g_5b, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 854#' 855#' g_6 <- make_lattice(c(2,2,2)) 856#' plot(g_6, layout=layout_with_dh) 857#' 858#' g_7 <- graph_from_literal(1:3:5 -- 2:4:6) 859#' plot(g_7, layout=layout_with_dh, vertex.label=V(g_7)$name) 860#' 861#' g_8 <- make_ring(5) + make_ring(10) + make_ring(5) + 862#' edges(1,6, 2,8, 3, 10, 4,12, 5,14, 863#' 7,16, 9,17, 11,18, 13,19, 15,20) 864#' plot(g_8, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 865#' 866#' g_9 <- make_lattice(c(3,2,2)) 867#' plot(g_9, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 868#' 869#' g_10 <- make_lattice(c(6,6)) 870#' plot(g_10, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 871#' 872#' g_11a <- make_tree(31, 2, mode="undirected") 873#' plot(g_11a, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 874#' 875#' g_11b <- make_tree(21, 4, mode="undirected") 876#' plot(g_11b, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 877#' 878#' g_12 <- make_empty_graph(n=37, directed=FALSE) + 879#' path(1:5,10,22,31,37:33,27,16,6,1) + path(6,7,11,9,10) + path(16:22) + 880#' path(27:31) + path(2,7,18,28,34) + path(3,8,11,19,29,32,35) + 881#' path(4,9,20,30,36) + path(1,7,12,14,19,24,26,30,37) + 882#' path(5,9,13,15,19,23,25,28,33) + path(3,12,16,25,35,26,22,13,3) 883#' plot(g_12, layout=layout_with_dh, vertex.size=5, vertex.label=NA) 884 885layout_with_dh <- function(graph, coords=NULL, maxiter=10, 886 fineiter=max(10, log2(vcount(graph))), cool.fact=0.75, 887 weight.node.dist=1.0, weight.border=0.0, 888 weight.edge.lengths=edge_density(graph) / 10, 889 weight.edge.crossings=1.0 - sqrt(edge_density(graph)), 890 weight.node.edge.dist=0.2 * (1-edge_density(graph))) { 891 892 # Argument checks 893 if (!is_igraph(graph)) { stop("Not a graph object") } 894 if (!is.null(coords)) { 895 coords <- as.matrix(structure(as.double(coords), dim=dim(coords))) 896 use.seed <- TRUE 897 } else { 898 coords <- matrix(NA_real_, ncol=2, nrow=0) 899 use.seed <- FALSE 900 } 901 maxiter <- as.integer(maxiter) 902 fineiter <- as.integer(fineiter) 903 cool.fact <- as.numeric(cool.fact) 904 weight.node.dist <- as.numeric(weight.node.dist) 905 weight.border <- as.numeric(weight.border) 906 weight.edge.lengths <- as.numeric(weight.edge.lengths) 907 weight.edge.crossings <- as.numeric(weight.edge.crossings) 908 weight.node.edge.dist <- as.numeric(weight.node.edge.dist) 909 910 on.exit(.Call(C_R_igraph_finalizer) ) 911 # Function call 912 res <- .Call(C_R_igraph_layout_davidson_harel, graph, coords, use.seed, 913 maxiter, fineiter, cool.fact, weight.node.dist, 914 weight.border, weight.edge.lengths, weight.edge.crossings, 915 weight.node.edge.dist) 916 917 res 918} 919 920 921#' @rdname layout_with_dh 922#' @param ... Passed to \code{layout_with_dh}. 923#' @export 924 925with_dh <- function(...) layout_spec(layout_with_dh, ...) 926 927 928 929## ---------------------------------------------------------------- 930 931 932#' The Fruchterman-Reingold layout algorithm 933#' 934#' Place vertices on the plane using the force-directed layout algorithm by 935#' Fruchterman and Reingold. 936#' 937#' See the referenced paper below for the details of the algorithm. 938#' 939#' This function was rewritten from scratch in igraph version 0.8.0. 940#' 941#' @param graph The graph to lay out. Edge directions are ignored. 942#' @param coords Optional starting positions for the vertices. If this argument 943#' is not \code{NULL} then it should be an appropriate matrix of starting 944#' coordinates. 945#' @param dim Integer scalar, 2 or 3, the dimension of the layout. Two 946#' dimensional layouts are places on a plane, three dimensional ones in the 3d 947#' space. 948#' @param niter Integer scalar, the number of iterations to perform. 949#' @param start.temp Real scalar, the start temperature. This is the maximum 950#' amount of movement alloved along one axis, within one step, for a vertex. 951#' Currently it is decreased linearly to zero during the iteration. 952#' @param grid Character scalar, whether to use the faster, but less accurate 953#' grid based implementation of the algorithm. By default (\dQuote{auto}), the 954#' grid-based implementation is used if the graph has more than one thousand 955#' vertices. 956#' @param weights A vector giving edge weights. The \code{weight} edge 957#' attribute is used by default, if present. If weights are given, then the 958#' attraction along the edges will be multiplied by the given edge weights. 959#' This places vertices connected with a highly weighted edge closer to 960#' each other. 961#' @param minx If not \code{NULL}, then it must be a numeric vector that gives 962#' lower boundaries for the \sQuote{x} coordinates of the vertices. The length 963#' of the vector must match the number of vertices in the graph. 964#' @param maxx Similar to \code{minx}, but gives the upper boundaries. 965#' @param miny Similar to \code{minx}, but gives the lower boundaries of the 966#' \sQuote{y} coordinates. 967#' @param maxy Similar to \code{minx}, but gives the upper boundaries of the 968#' \sQuote{y} coordinates. 969#' @param minz Similar to \code{minx}, but gives the lower boundaries of the 970#' \sQuote{z} coordinates. 971#' @param maxz Similar to \code{minx}, but gives the upper boundaries of the 972#' \sQuote{z} coordinates. 973#' @param coolexp,maxdelta,area,repulserad These arguments are not supported 974#' from igraph version 0.8.0 and are ignored (with a warning). 975#' @param maxiter A deprecated synonym of \code{niter}, for compatibility. 976#' @return A two- or three-column matrix, each row giving the coordinates of a 977#' vertex, according to the ids of the vertex ids. 978#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 979#' @seealso \code{\link{layout_with_drl}}, \code{\link{layout_with_kk}} for 980#' other layout algorithms. 981#' @references Fruchterman, T.M.J. and Reingold, E.M. (1991). Graph Drawing by 982#' Force-directed Placement. \emph{Software - Practice and Experience}, 983#' 21(11):1129-1164. 984#' @export 985#' @family graph layouts 986#' @keywords graphs 987#' @examples 988#' 989#' # Fixing ego 990#' g <- sample_pa(20, m=2) 991#' minC <- rep(-Inf, vcount(g)) 992#' maxC <- rep(Inf, vcount(g)) 993#' minC[1] <- maxC[1] <- 0 994#' co <- layout_with_fr(g, minx=minC, maxx=maxC, 995#' miny=minC, maxy=maxC) 996#' co[1,] 997#' plot(g, layout=co, vertex.size=30, edge.arrow.size=0.2, 998#' vertex.label=c("ego", rep("", vcount(g)-1)), rescale=FALSE, 999#' xlim=range(co[,1]), ylim=range(co[,2]), vertex.label.dist=0, 1000#' vertex.label.color="red") 1001#' axis(1) 1002#' axis(2) 1003#' 1004layout_with_fr <- function(graph, coords=NULL, dim=2, 1005 niter=500, start.temp=sqrt(vcount(graph)), 1006 grid=c("auto", "grid", "nogrid"), weights=NULL, 1007 minx=NULL, maxx=NULL, miny=NULL, maxy=NULL, 1008 minz=NULL, maxz=NULL, 1009 coolexp, maxdelta, area, repulserad, maxiter) { 1010 1011 # Argument checks 1012 if (!is_igraph(graph)) { stop("Not a graph object") } 1013 if (!is.null(coords)) { 1014 coords <- as.matrix(structure(as.double(coords), dim=dim(coords))) 1015 } 1016 dim <- as.integer(dim) 1017 if (dim != 2L && dim != 3L) { 1018 stop("Dimension must be two or three") 1019 } 1020 if (!missing(niter) && !missing(maxiter)) { 1021 stop("Both `niter' and `maxiter' are given, give only one of them") 1022 } 1023 if (!missing(maxiter)) niter <- maxiter 1024 niter <- as.integer(niter) 1025 start.temp <- as.numeric(start.temp) 1026 1027 grid <- igraph.match.arg(grid) 1028 grid <- switch(grid, "grid"=0L, "nogrid"=1L, "auto"=2L) 1029 1030 if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { 1031 weights <- E(graph)$weight 1032 } 1033 if (!is.null(weights) && any(!is.na(weights))) { 1034 weights <- as.numeric(weights) 1035 } else { 1036 weights <- NULL 1037 } 1038 if (!is.null(minx)) minx <- as.numeric(minx) 1039 if (!is.null(maxx)) maxx <- as.numeric(maxx) 1040 if (!is.null(miny)) miny <- as.numeric(miny) 1041 if (!is.null(maxy)) maxy <- as.numeric(maxy) 1042 if (!is.null(minz)) minz <- as.numeric(minz) 1043 if (!is.null(maxz)) maxz <- as.numeric(maxz) 1044 if (!missing(coolexp)) { 1045 warning("Argument `coolexp' is deprecated and has no effect") 1046 } 1047 if (!missing(maxdelta)) { 1048 warning("Argument `maxdelta' is deprecated and has no effect") 1049 } 1050 if (!missing(area)) { 1051 warning("Argument `area' is deprecated and has no effect") 1052 } 1053 if (!missing(repulserad)) { 1054 warning("Argument `repulserad' is deprecated and has no effect") 1055 } 1056 1057 on.exit(.Call(C_R_igraph_finalizer) ) 1058 if (dim==2) { 1059 res <- .Call(C_R_igraph_layout_fruchterman_reingold, graph, coords, 1060 niter, start.temp, weights, minx, maxx, miny, maxy, grid) 1061 } else { 1062 res <- .Call(C_R_igraph_layout_fruchterman_reingold_3d, graph, coords, 1063 niter, start.temp, weights, minx, maxx, miny, maxy, 1064 minz, maxz) 1065 } 1066 res 1067} 1068 1069 1070#' @rdname layout_with_fr 1071#' @param ... Passed to \code{layout_with_fr}. 1072#' @export 1073 1074with_fr <- function(...) layout_spec(layout_with_fr, ...) 1075 1076#' @export 1077#' @rdname layout.deprecated 1078 1079layout.fruchterman.reingold <- function(..., params = list()) { 1080 do_call(layout_with_fr, .args = c(list(...), params)) 1081} 1082 1083## ---------------------------------------------------------------- 1084 1085 1086#' The GEM layout algorithm 1087#' 1088#' Place vertices on the plane using the GEM force-directed layout algorithm. 1089#' 1090#' See the referenced paper below for the details of the algorithm. 1091#' 1092#' @aliases layout.gem 1093#' @param graph The input graph. Edge directions are ignored. 1094#' @param coords If not \code{NULL}, then the starting coordinates should be 1095#' given here, in a two or three column matrix, depending on the \code{dim} 1096#' argument. 1097#' @param maxiter The maximum number of iterations to perform. Updating a 1098#' single vertex counts as an iteration. A reasonable default is 40 * n * n, 1099#' where n is the number of vertices. The original paper suggests 4 * n * n, 1100#' but this usually only works if the other parameters are set up carefully. 1101#' @param temp.max The maximum allowed local temperature. A reasonable default 1102#' is the number of vertices. 1103#' @param temp.min The global temperature at which the algorithm terminates 1104#' (even before reaching \code{maxiter} iterations). A reasonable default is 1105#' 1/10. 1106#' @param temp.init Initial local temperature of all vertices. A reasonable 1107#' default is the square root of the number of vertices. 1108#' @return A numeric matrix with two columns, and as many rows as the number of 1109#' vertices. 1110#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 1111#' @seealso \code{\link{layout_with_fr}}, 1112#' \code{\link{plot.igraph}}, \code{\link{tkplot}} 1113#' @references Arne Frick, Andreas Ludwig, Heiko Mehldau: A Fast Adaptive 1114#' Layout Algorithm for Undirected Graphs, \emph{Proc. Graph Drawing 1994}, 1115#' LNCS 894, pp. 388-403, 1995. 1116#' @export 1117#' @family graph layouts 1118#' @keywords graphs 1119#' @examples 1120#' 1121#' set.seed(42) 1122#' g <- make_ring(10) 1123#' plot(g, layout=layout_with_gem) 1124#' 1125layout_with_gem <- function(graph, coords=NULL, maxiter=40*vcount(graph)^2, 1126 temp.max=vcount(graph), temp.min=1/10, 1127 temp.init=sqrt(vcount(graph))) { 1128 1129 # Argument checks 1130 if (!is_igraph(graph)) { stop("Not a graph object") } 1131 if (!is.null(coords)) { 1132 coords <- as.matrix(structure(as.double(coords), dim=dim(coords))) 1133 use.seed <- TRUE 1134 } else { 1135 coords <- matrix(NA_real_, ncol=2, nrow=0) 1136 use.seed <- FALSE 1137 } 1138 1139 maxiter <- as.integer(maxiter) 1140 temp.max <- as.numeric(temp.max) 1141 temp.min <- as.numeric(temp.min) 1142 temp.init <- as.numeric(temp.init) 1143 1144 on.exit(.Call(C_R_igraph_finalizer) ) 1145 # Function call 1146 res <- .Call(C_R_igraph_layout_gem, graph, coords, use.seed, maxiter, 1147 temp.max, temp.min, temp.init) 1148 1149 res 1150} 1151 1152 1153#' @rdname layout_with_gem 1154#' @param ... Passed to \code{layout_with_gem}. 1155#' @export 1156 1157with_gem <- function(...) layout_spec(layout_with_gem, ...) 1158 1159 1160## ---------------------------------------------------------------- 1161 1162 1163#' The graphopt layout algorithm 1164#' 1165#' A force-directed layout algorithm, that scales relatively well to large 1166#' graphs. 1167#' 1168#' \code{layout_with_graphopt} is a port of the graphopt layout algorithm by Michael 1169#' Schmuhl. graphopt version 0.4.1 was rewritten in C and the support for 1170#' layers was removed (might be added later) and a code was a bit reorganized 1171#' to avoid some unnecessary steps is the node charge (see below) is zero. 1172#' 1173#' graphopt uses physical analogies for defining attracting and repelling 1174#' forces among the vertices and then the physical system is simulated until it 1175#' reaches an equilibrium. (There is no simulated annealing or anything like 1176#' that, so a stable fixed point is not guaranteed.) 1177#' 1178#' See also \url{http://www.schmuhl.org/graphopt/} for the original graphopt. 1179#' 1180#' @aliases layout.graphopt 1181#' @param graph The input graph. 1182#' @param start If given, then it should be a matrix with two columns and one 1183#' line for each vertex. This matrix will be used as starting positions for the 1184#' algorithm. If not given, then a random starting matrix is used. 1185#' @param niter Integer scalar, the number of iterations to perform. Should be 1186#' a couple of hundred in general. If you have a large graph then you might 1187#' want to only do a few iterations and then check the result. If it is not 1188#' good enough you can feed it in again in the \code{start} argument. The 1189#' default value is 500. 1190#' @param charge The charge of the vertices, used to calculate electric 1191#' repulsion. The default is 0.001. 1192#' @param mass The mass of the vertices, used for the spring forces. The 1193#' default is 30. 1194#' @param spring.length The length of the springs, an integer number. The 1195#' default value is zero. 1196#' @param spring.constant The spring constant, the default value is one. 1197#' @param max.sa.movement Real constant, it gives the maximum amount of 1198#' movement allowed in a single step along a single axis. The default value is 1199#' 5. 1200#' @return A numeric matrix with two columns, and a row for each vertex. 1201#' @author Michael Schmuhl for the original graphopt code, rewritten and 1202#' wrapped by Gabor Csardi \email{csardi.gabor@@gmail.com}. 1203#' @keywords graphs 1204#' @export 1205#' @family graph layouts 1206 1207layout_with_graphopt <- function(graph, start=NULL, niter=500, charge=0.001, 1208 mass=30, spring.length=0, spring.constant=1, 1209 max.sa.movement=5) { 1210 1211 if (!is_igraph(graph)) { 1212 stop("Not a graph object") 1213 } 1214 if (!is.null(start)) { 1215 start <- structure(as.numeric(start), dim=dim(start)) 1216 } 1217 niter <- as.double(niter) 1218 charge <- as.double(charge) 1219 mass <- as.double(mass) 1220 spring.length <- as.double(spring.length) 1221 spring.constant <- as.double(spring.constant) 1222 max.sa.movement <- as.double(max.sa.movement) 1223 1224 on.exit(.Call(C_R_igraph_finalizer) ) 1225 .Call(C_R_igraph_layout_graphopt, graph, niter, charge, mass, 1226 spring.length, spring.constant, max.sa.movement, start) 1227} 1228 1229 1230#' @rdname layout_with_graphopt 1231#' @param ... Passed to \code{layout_with_graphopt}. 1232#' @export 1233 1234with_graphopt <- function(...) layout_spec(layout_with_graphopt, ...) 1235 1236 1237## ---------------------------------------------------------------- 1238 1239 1240#' The Kamada-Kawai layout algorithm 1241#' 1242#' Place the vertices on the plane, or in the 3d space, based on a physical 1243#' model of springs. 1244#' 1245#' See the referenced paper below for the details of the algorithm. 1246#' 1247#' This function was rewritten from scratch in igraph version 0.8.0 and it 1248#' follows truthfully the original publication by Kamada and Kawai now. 1249#' 1250#' @param graph The input graph. Edge directions are ignored. 1251#' @param coords If not \code{NULL}, then the starting coordinates should be 1252#' given here, in a two or three column matrix, depending on the \code{dim} 1253#' argument. 1254#' @param dim Integer scalar, 2 or 3, the dimension of the layout. Two 1255#' dimensional layouts are places on a plane, three dimensional ones in the 3d 1256#' space. 1257#' @param maxiter The maximum number of iterations to perform. The algorithm 1258#' might terminate earlier, see the \code{epsilon} argument. 1259#' @param epsilon Numeric scalar, the algorithm terminates, if the maximal 1260#' delta is less than this. (See the reference below for what delta means.) If 1261#' you set this to zero, then the function always performs \code{maxiter} 1262#' iterations. 1263#' @param kkconst Numeric scalar, the Kamada-Kawai vertex attraction constant. 1264#' Typical (and default) value is the number of vertices. 1265#' @param weights Edge weights, larger values will result longer edges. 1266#' Note that this is opposite to \code{\link{layout_with_fr}}. 1267#' @param minx If not \code{NULL}, then it must be a numeric vector that gives 1268#' lower boundaries for the \sQuote{x} coordinates of the vertices. The length 1269#' of the vector must match the number of vertices in the graph. 1270#' @param maxx Similar to \code{minx}, but gives the upper boundaries. 1271#' @param miny Similar to \code{minx}, but gives the lower boundaries of the 1272#' \sQuote{y} coordinates. 1273#' @param maxy Similar to \code{minx}, but gives the upper boundaries of the 1274#' \sQuote{y} coordinates. 1275#' @param minz Similar to \code{minx}, but gives the lower boundaries of the 1276#' \sQuote{z} coordinates. 1277#' @param maxz Similar to \code{minx}, but gives the upper boundaries of the 1278#' \sQuote{z} coordinates. 1279#' @param niter,sigma,initemp,coolexp These arguments are not supported from 1280#' igraph version 0.8.0 and are ignored (with a warning). 1281#' @param start Deprecated synonym for \code{coords}, for compatibility. 1282#' @return A numeric matrix with two (dim=2) or three (dim=3) columns, and as 1283#' many rows as the number of vertices, the x, y and potentially z coordinates 1284#' of the vertices. 1285#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 1286#' @seealso \code{\link{layout_with_drl}}, \code{\link{plot.igraph}}, 1287#' \code{\link{tkplot}} 1288#' @references Kamada, T. and Kawai, S.: An Algorithm for Drawing General 1289#' Undirected Graphs. \emph{Information Processing Letters}, 31/1, 7--15, 1989. 1290#' @export 1291#' @family graph layouts 1292#' @keywords graphs 1293#' @examples 1294#' 1295#' g <- make_ring(10) 1296#' E(g)$weight <- rep(1:2, length.out=ecount(g)) 1297#' plot(g, layout=layout_with_kk, edge.label=E(g)$weight) 1298#' 1299layout_with_kk <- function(graph, coords=NULL, dim=2, 1300 maxiter=50*vcount(graph), 1301 epsilon=0.0, kkconst=vcount(graph), 1302 weights=NULL, minx=NULL, maxx=NULL, 1303 miny=NULL, maxy=NULL, minz=NULL, maxz=NULL, 1304 niter, sigma, initemp, coolexp, start) { 1305 # Argument checks 1306 if (!missing(coords) && !missing(start)) { 1307 stop("Both `coords' and `start' are given, give only one of them.") 1308 } 1309 if (!missing(start)) coords <- start 1310 1311 if (!is_igraph(graph)) { stop("Not a graph object") } 1312 if (!is.null(coords)) { 1313 coords <- as.matrix(structure(as.double(coords), dim=dim(coords))) 1314 } 1315 dim <- as.integer(dim) 1316 if (dim != 2L && dim != 3L) { 1317 stop("Dimension must be two or three") 1318 } 1319 1320 maxiter <- as.integer(maxiter) 1321 epsilon <- as.numeric(epsilon) 1322 kkconst <- as.numeric(kkconst) 1323 if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { 1324 weights <- E(graph)$weight 1325 } 1326 if (!is.null(weights) && any(!is.na(weights))) { 1327 weights <- as.numeric(weights) 1328 } else { 1329 weights <- NULL 1330 } 1331 if (!is.null(minx)) minx <- as.numeric(minx) 1332 if (!is.null(maxx)) maxx <- as.numeric(maxx) 1333 if (!is.null(miny)) miny <- as.numeric(miny) 1334 if (!is.null(maxy)) maxy <- as.numeric(maxy) 1335 if (!is.null(minz)) minz <- as.numeric(minz) 1336 if (!is.null(maxz)) maxz <- as.numeric(maxz) 1337 1338 if (!missing(niter)) { 1339 warning("Argument `niter' is deprecated and has no effect") 1340 } 1341 if (!missing(sigma)) { 1342 warning("Argument `sigma' is deprecated and has no effect") 1343 } 1344 if (!missing(initemp)) { 1345 warning("Argument `initemp' is deprecated and has no effect") 1346 } 1347 if (!missing(coolexp)) { 1348 warning("Argument `coolexp' is deprecated and has no effect") 1349 } 1350 1351 on.exit(.Call(C_R_igraph_finalizer) ) 1352 # Function call 1353 if (dim == 2) { 1354 res <- .Call(C_R_igraph_layout_kamada_kawai, graph, coords, maxiter, 1355 epsilon, kkconst, weights, minx, maxx, miny, maxy) 1356 } else { 1357 res <- .Call(C_R_igraph_layout_kamada_kawai_3d, graph, coords, maxiter, 1358 epsilon, kkconst, weights, minx, maxx, miny, maxy, minz, 1359 maxz) 1360 } 1361 1362 res 1363} 1364 1365 1366#' @rdname layout_with_kk 1367#' @param ... Passed to \code{layout_with_kk}. 1368#' @export 1369#' 1370with_kk <- function(...) layout_spec(layout_with_kk, ...) 1371 1372#' @export 1373#' @rdname layout.deprecated 1374 1375layout.kamada.kawai <- function(..., params = list()) { 1376 do_call(layout_with_kk, .args = c(list(...), params)) 1377} 1378 1379## ---------------------------------------------------------------- 1380 1381 1382#' Large Graph Layout 1383#' 1384#' A layout generator for larger graphs. 1385#' 1386#' \code{layout_with_lgl} is for large connected graphs, it is similar to the layout 1387#' generator of the Large Graph Layout software 1388#' (\url{http://lgl.sourceforge.net/}). 1389#' 1390#' @param graph The input graph 1391#' @param maxiter The maximum number of iterations to perform (150). 1392#' @param maxdelta The maximum change for a vertex during an iteration (the 1393#' number of vertices). 1394#' @param area The area of the surface on which the vertices are placed (square 1395#' of the number of vertices). 1396#' @param coolexp The cooling exponent of the simulated annealing (1.5). 1397#' @param repulserad Cancellation radius for the repulsion (the \code{area} 1398#' times the number of vertices). 1399#' @param cellsize The size of the cells for the grid. When calculating the 1400#' repulsion forces between vertices only vertices in the same or neighboring 1401#' grid cells are taken into account (the fourth root of the number of 1402#' \code{area}. 1403#' @param root The id of the vertex to place at the middle of the layout. The 1404#' default value is -1 which means that a random vertex is selected. 1405#' @return A numeric matrix with two columns and as many rows as vertices. 1406#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 1407#' @keywords graphs 1408#' @export 1409#' @family graph layouts 1410 1411layout_with_lgl <- function(graph, maxiter=150, maxdelta=vcount(graph), 1412 area=vcount(graph)^2, coolexp=1.5, 1413 repulserad=area * vcount(graph), 1414 cellsize=sqrt(sqrt(area)), root=NULL) { 1415 1416 if (!is_igraph(graph)) { 1417 stop("Not a graph object") 1418 } 1419 if (is.null(root)) { 1420 root <- -1 1421 } else { 1422 root <- as.igraph.vs(graph, root)-1 1423 } 1424 1425 on.exit(.Call(C_R_igraph_finalizer) ) 1426 .Call(C_R_igraph_layout_lgl, graph, as.double(maxiter), 1427 as.double(maxdelta), as.double(area), as.double(coolexp), 1428 as.double(repulserad), as.double(cellsize), root) 1429} 1430 1431 1432#' @rdname layout_with_lgl 1433#' @param ... Passed to \code{layout_with_lgl}. 1434#' @export 1435 1436with_lgl <- function(...) layout_spec(layout_with_lgl, ...) 1437 1438#' @export 1439#' @rdname layout.deprecated 1440 1441layout.lgl <- function(..., params = list()) { 1442 do_call(layout_with_lgl, .args = c(list(...), params)) 1443} 1444 1445## ---------------------------------------------------------------- 1446 1447 1448 1449#' Graph layout by multidimensional scaling 1450#' 1451#' Multidimensional scaling of some distance matrix defined on the vertices of 1452#' a graph. 1453#' 1454#' \code{layout_with_mds} uses metric multidimensional scaling for generating the 1455#' coordinates. Multidimensional scaling aims to place points from a higher 1456#' dimensional space in a (typically) 2 dimensional plane, so that the distance 1457#' between the points are kept as much as this is possible. 1458#' 1459#' By default igraph uses the shortest path matrix as the distances between the 1460#' nodes, but the user can override this via the \code{dist} argument. 1461#' 1462#' This function generates the layout separately for each graph component and 1463#' then merges them via \code{\link{merge_coords}}. 1464#' 1465#' @aliases layout.mds 1466#' @param graph The input graph. 1467#' @param dist The distance matrix for the multidimensional scaling. If 1468#' \code{NULL} (the default), then the unweighted shortest path matrix is used. 1469#' @param dim \code{layout_with_mds} supports dimensions up to the number of nodes 1470#' minus one, but only if the graph is connected; for unconnected graphs, the 1471#' only possible values is 2. This is because \code{merge_coords} only works in 1472#' 2D. 1473#' @param options This is currently ignored, as ARPACK is not used any more for 1474#' solving the eigenproblem 1475#' @return A numeric matrix with \code{dim} columns. 1476#' @author Tamas Nepusz \email{ntamas@@gmail.com} and Gabor Csardi 1477#' \email{csardi.gabor@@gmail.com} 1478#' @seealso \code{\link{layout}}, \code{\link{plot.igraph}} 1479#' @references Cox, T. F. and Cox, M. A. A. (2001) \emph{Multidimensional 1480#' Scaling}. Second edition. Chapman and Hall. 1481#' @export 1482#' @family graph layouts 1483#' @keywords graphs 1484#' @examples 1485#' 1486#' g <- sample_gnp(100, 2/100) 1487#' l <- layout_with_mds(g) 1488#' plot(g, layout=l, vertex.label=NA, vertex.size=3) 1489 1490layout_with_mds <- function(graph, dist=NULL, dim=2, 1491 options=arpack_defaults) { 1492 1493 # Argument checks 1494 if (!is_igraph(graph)) { stop("Not a graph object") } 1495 if (!is.null(dist)) dist <- structure(as.double(dist), dim=dim(dist)) 1496 dim <- as.integer(dim) 1497 1498 on.exit(.Call(C_R_igraph_finalizer) ) 1499 # Function call 1500 res <- .Call(C_R_igraph_layout_mds, graph, dist, dim) 1501 1502 res 1503} 1504 1505 1506#' @rdname layout_with_mds 1507#' @param ... Passed to \code{layout_with_mds}. 1508#' @export 1509 1510with_mds <- function(...) layout_spec(layout_with_mds, ...) 1511 1512 1513## ---------------------------------------------------------------- 1514 1515 1516#' The Sugiyama graph layout generator 1517#' 1518#' Sugiyama layout algorithm for layered directed acyclic graphs. The algorithm 1519#' minimized edge crossings. 1520#' 1521#' This layout algorithm is designed for directed acyclic graphs where each 1522#' vertex is assigned to a layer. Layers are indexed from zero, and vertices of 1523#' the same layer will be placed on the same horizontal line. The X coordinates 1524#' of vertices within each layer are decided by the heuristic proposed by 1525#' Sugiyama et al. to minimize edge crossings. 1526#' 1527#' You can also try to lay out undirected graphs, graphs containing cycles, or 1528#' graphs without an a priori layered assignment with this algorithm. igraph 1529#' will try to eliminate cycles and assign vertices to layers, but there is no 1530#' guarantee on the quality of the layout in such cases. 1531#' 1532#' The Sugiyama layout may introduce \dQuote{bends} on the edges in order to 1533#' obtain a visually more pleasing layout. This is achieved by adding dummy 1534#' nodes to edges spanning more than one layer. The resulting layout assigns 1535#' coordinates not only to the nodes of the original graph but also to the 1536#' dummy nodes. The layout algorithm will also return the extended graph with 1537#' the dummy nodes. 1538#' 1539#' For more details, see the reference below. 1540#' 1541#' @aliases layout.sugiyama 1542#' @param graph The input graph. 1543#' @param layers A numeric vector or \code{NULL}. If not \code{NULL}, then it 1544#' should specify the layer index of the vertices. Layers are numbered from 1545#' one. If \code{NULL}, then igraph calculates the layers automatically. 1546#' @param hgap Real scalar, the minimum horizontal gap between vertices in the 1547#' same layer. 1548#' @param vgap Real scalar, the distance between layers. 1549#' @param maxiter Integer scalar, the maximum number of iterations in the 1550#' crossing minimization stage. 100 is a reasonable default; if you feel that 1551#' you have too many edge crossings, increase this. 1552#' @param weights Optional edge weight vector. If \code{NULL}, then the 1553#' 'weight' edge attribute is used, if there is one. Supply \code{NA} here and 1554#' igraph ignores the edge weights. These are used only if the graph 1555#' contains cycles; igraph will tend to reverse edges with smaller weights 1556#' when breaking the cycles. 1557#' @param attributes Which graph/vertex/edge attributes to keep in the extended 1558#' graph. \sQuote{default} keeps the \sQuote{size}, \sQuote{size2}, 1559#' \sQuote{shape}, \sQuote{label} and \sQuote{color} vertex attributes and the 1560#' \sQuote{arrow.mode} and \sQuote{arrow.size} edge attributes. \sQuote{all} 1561#' keep all graph, vertex and edge attributes, \sQuote{none} keeps none of 1562#' them. 1563#' @return A list with the components: \item{layout}{The layout, a two-column 1564#' matrix, for the original graph vertices.} \item{layout.dummy}{The layout for 1565#' the dummy vertices, a two column matrix.} \item{extd_graph}{The original 1566#' graph, extended with dummy vertices. The \sQuote{dummy} vertex attribute is 1567#' set on this graph, it is a logical attributes, and it tells you whether the 1568#' vertex is a dummy vertex. The \sQuote{layout} graph attribute is also set, 1569#' and it is the layout matrix for all (original and dummy) vertices.} 1570#' @author Tamas Nepusz \email{ntamas@@gmail.com} 1571#' @references K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual 1572#' Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man 1573#' and Cybernetics 11(2):109-125, 1981. 1574#' @export 1575#' @importFrom utils head 1576#' @family graph layouts 1577#' @keywords graphs 1578#' @examples 1579#' 1580#' ## Data taken from http://tehnick-8.narod.ru/dc_clients/ 1581#' DC <- graph_from_literal("DC++" -+ 1582#' "LinuxDC++":"BCDC++":"EiskaltDC++":"StrongDC++":"DiCe!++", 1583#' "LinuxDC++" -+ "FreeDC++", "BCDC++" -+ "StrongDC++", 1584#' "FreeDC++" -+ "BMDC++":"EiskaltDC++", 1585#' "StrongDC++" -+ "AirDC++":"zK++":"ApexDC++":"TkDC++", 1586#' "StrongDC++" -+ "StrongDC++ SQLite":"RSX++", 1587#' "ApexDC++" -+ "FlylinkDC++ ver <= 4xx", 1588#' "ApexDC++" -+ "ApexDC++ Speed-Mod":"DiCe!++", 1589#' "StrongDC++ SQLite" -+ "FlylinkDC++ ver >= 5xx", 1590#' "ApexDC++ Speed-Mod" -+ "FlylinkDC++ ver <= 4xx", 1591#' "ApexDC++ Speed-Mod" -+ "GreylinkDC++", 1592#' "FlylinkDC++ ver <= 4xx" -+ "FlylinkDC++ ver >= 5xx", 1593#' "FlylinkDC++ ver <= 4xx" -+ AvaLink, 1594#' "GreylinkDC++" -+ AvaLink:"RayLinkDC++":"SparkDC++":PeLink) 1595#' 1596#' ## Use edge types 1597#' E(DC)$lty <- 1 1598#' E(DC)["BCDC++" %->% "StrongDC++"]$lty <- 2 1599#' E(DC)["FreeDC++" %->% "EiskaltDC++"]$lty <- 2 1600#' E(DC)["ApexDC++" %->% "FlylinkDC++ ver <= 4xx"]$lty <- 2 1601#' E(DC)["ApexDC++" %->% "DiCe!++"]$lty <- 2 1602#' E(DC)["StrongDC++ SQLite" %->% "FlylinkDC++ ver >= 5xx"]$lty <- 2 1603#' E(DC)["GreylinkDC++" %->% "AvaLink"]$lty <- 2 1604#' 1605#' ## Layers, as on the plot 1606#' layers <- list(c("DC++"), 1607#' c("LinuxDC++", "BCDC++"), 1608#' c("FreeDC++", "StrongDC++"), 1609#' c("BMDC++", "EiskaltDC++", "AirDC++", "zK++", "ApexDC++", 1610#' "TkDC++", "RSX++"), 1611#' c("StrongDC++ SQLite", "ApexDC++ Speed-Mod", "DiCe!++"), 1612#' c("FlylinkDC++ ver <= 4xx", "GreylinkDC++"), 1613#' c("FlylinkDC++ ver >= 5xx", "AvaLink", "RayLinkDC++", 1614#' "SparkDC++", "PeLink")) 1615#' 1616#' ## Check that we have all nodes 1617#' all(sort(unlist(layers)) == sort(V(DC)$name)) 1618#' 1619#' ## Add some graphical parameters 1620#' V(DC)$color <- "white" 1621#' V(DC)$shape <- "rectangle" 1622#' V(DC)$size <- 20 1623#' V(DC)$size2 <- 10 1624#' V(DC)$label <- lapply(V(DC)$name, function(x) 1625#' paste(strwrap(x, 12), collapse="\n")) 1626#' E(DC)$arrow.size <- 0.5 1627#' 1628#' ## Create a similar layout using the predefined layers 1629#' lay1 <- layout_with_sugiyama(DC, layers=apply(sapply(layers, 1630#' function(x) V(DC)$name %in% x), 1, which)) 1631#' 1632#' ## Simple plot, not very nice 1633#' par(mar=rep(.1, 4)) 1634#' plot(DC, layout=lay1$layout, vertex.label.cex=0.5) 1635#' 1636#' ## Sugiyama plot 1637#' plot(lay1$extd_graph, vertex.label.cex=0.5) 1638#' 1639#' ## The same with automatic layer calculation 1640#' ## Keep vertex/edge attributes in the extended graph 1641#' lay2 <- layout_with_sugiyama(DC, attributes="all") 1642#' plot(lay2$extd_graph, vertex.label.cex=0.5) 1643#' 1644#' ## Another example, from the following paper: 1645#' ## Markus Eiglsperger, Martin Siebenhaller, Michael Kaufmann: 1646#' ## An Efficient Implementation of Sugiyama's Algorithm for 1647#' ## Layered Graph Drawing, Journal of Graph Algorithms and 1648#' ## Applications 9, 305--325 (2005). 1649#' 1650#' ex <- graph_from_literal( 0 -+ 29: 6: 5:20: 4, 1651#' 1 -+ 12, 1652#' 2 -+ 23: 8, 1653#' 3 -+ 4, 1654#' 4, 1655#' 5 -+ 2:10:14:26: 4: 3, 1656#' 6 -+ 9:29:25:21:13, 1657#' 7, 1658#' 8 -+ 20:16, 1659#' 9 -+ 28: 4, 1660#' 10 -+ 27, 1661#' 11 -+ 9:16, 1662#' 12 -+ 9:19, 1663#' 13 -+ 20, 1664#' 14 -+ 10, 1665#' 15 -+ 16:27, 1666#' 16 -+ 27, 1667#' 17 -+ 3, 1668#' 18 -+ 13, 1669#' 19 -+ 9, 1670#' 20 -+ 4, 1671#' 21 -+ 22, 1672#' 22 -+ 8: 9, 1673#' 23 -+ 9:24, 1674#' 24 -+ 12:15:28, 1675#' 25 -+ 11, 1676#' 26 -+ 18, 1677#' 27 -+ 13:19, 1678#' 28 -+ 7, 1679#' 29 -+ 25 ) 1680#' 1681#' layers <- list( 0, c(5, 17), c(2, 14, 26, 3), c(23, 10, 18), c(1, 24), 1682#' 12, 6, c(29,21), c(25,22), c(11,8,15), 16, 27, c(13,19), 1683#' c(9, 20), c(4, 28), 7 ) 1684#' 1685#' layex <- layout_with_sugiyama(ex, layers=apply(sapply(layers, 1686#' function(x) V(ex)$name %in% as.character(x)), 1687#' 1, which)) 1688#' 1689#' origvert <- c(rep(TRUE, vcount(ex)), rep(FALSE, nrow(layex$layout.dummy))) 1690#' realedge <- as_edgelist(layex$extd_graph)[,2] <= vcount(ex) 1691#' plot(layex$extd_graph, vertex.label.cex=0.5, 1692#' edge.arrow.size=.5, 1693#' vertex.size=ifelse(origvert, 5, 0), 1694#' vertex.shape=ifelse(origvert, "square", "none"), 1695#' vertex.label=ifelse(origvert, V(ex)$name, ""), 1696#' edge.arrow.mode=ifelse(realedge, 2, 0)) 1697#' 1698 layout_with_sugiyama <- function(graph, layers=NULL, hgap=1, vgap=1, 1699 maxiter=100, weights=NULL, 1700 attributes=c("default", "all", "none")) { 1701 # Argument checks 1702 if (!is_igraph(graph)) { stop("Not a graph object") } 1703 if (!is.null(layers)) layers <- as.numeric(layers)-1 1704 hgap <- as.numeric(hgap) 1705 vgap <- as.numeric(vgap) 1706 maxiter <- as.integer(maxiter) 1707 if (is.null(weights) && "weight" %in% edge_attr_names(graph)) { 1708 weights <- E(graph)$weight 1709 } 1710 if (!is.null(weights) && any(!is.na(weights))) { 1711 weights <- as.numeric(weights) 1712 } else { 1713 weights <- NULL 1714 } 1715 attributes <- igraph.match.arg(attributes) 1716 1717 on.exit(.Call(C_R_igraph_finalizer) ) 1718 # Function call 1719 res <- .Call(C_R_igraph_layout_sugiyama, graph, layers, hgap, 1720 vgap, maxiter, weights) 1721 1722 # Flip the y coordinates, more natural this way 1723 res$res[,2] <- max(res$res[,2]) - res$res[,2] + 1 1724 1725 # Separate real and dummy vertices 1726 vc <- vcount(graph) 1727 if (nrow(res$res)==vc) { 1728 res$layout <- res$res 1729 res$layout.dummy <- matrix(NA_real_, nrow=0, ncol=2) 1730 } else { 1731 res$layout <- res$res[seq_len(vc),] 1732 res$layout.dummy <- res$res[(vc+1):nrow(res$res),] 1733 } 1734 1735 # Add some attributes to the extended graph 1736 E(res$extd_graph)$orig <- res$extd_to_orig_eids 1737 res$extd_to_orig_eids <- NULL 1738 1739 res$extd_graph <- set_vertex_attr(res$extd_graph, "dummy", 1740 value=c(rep(FALSE, vc), 1741 rep(TRUE, nrow(res$res)-vc))) 1742 1743 res$extd_graph$layout <- rbind(res$layout, res$layout.dummy) 1744 1745 if (attributes=="default" || attributes=="all") { 1746 if ("size" %in% vertex_attr_names(graph)) { 1747 V(res$extd_graph)$size <- 0 1748 V(res$extd_graph)$size[ !V(res$extd_graph)$dummy ] <- V(graph)$size 1749 } 1750 if ("size2" %in% vertex_attr_names(graph)) { 1751 V(res$extd_graph)$size2 <- 0 1752 V(res$extd_graph)$size2[ !V(res$extd_graph)$dummy ] <- V(graph)$size2 1753 } 1754 if ("shape" %in% vertex_attr_names(graph)) { 1755 V(res$extd_graph)$shape <- "none" 1756 V(res$extd_graph)$shape[ !V(res$extd_graph)$dummy ] <- V(graph)$shape 1757 } 1758 if ("label" %in% vertex_attr_names(graph)) { 1759 V(res$extd_graph)$label <- "" 1760 V(res$extd_graph)$label[ !V(res$extd_graph)$dummy ] <- V(graph)$label 1761 } 1762 if ("color" %in% vertex_attr_names(graph)) { 1763 V(res$extd_graph)$color <- head(V(graph)$color, 1) 1764 V(res$extd_graph)$color[ !V(res$extd_graph)$dummy ] <- V(graph)$color 1765 } 1766 eetar <- as_edgelist(res$extd_graph, names=FALSE)[,2] 1767 E(res$extd_graph)$arrow.mode <- 0 1768 if ("arrow.mode" %in% edge_attr_names(graph)) { 1769 E(res$extd_graph)$arrow.mode[ eetar <= vc ] <- E(graph)$arrow.mode 1770 } else { 1771 E(res$extd_graph)$arrow.mode[ eetar <= vc ] <- is_directed(graph) * 2 1772 } 1773 if ("arrow.size" %in% edge_attr_names(graph)) { 1774 E(res$extd_graph)$arrow.size <- 0 1775 E(res$extd_graph)$arrow.size[ eetar <= vc ] <- E(graph)$arrow.size 1776 } 1777 } 1778 1779 if (attributes=="all") { 1780 gatt <- setdiff(graph_attr_names(graph), "layout") 1781 vatt <- setdiff(vertex_attr_names(graph), 1782 c("size", "size2", "shape", "label", "color")) 1783 eatt <- setdiff(edge_attr_names(graph), 1784 c("arrow.mode", "arrow.size")) 1785 for (ga in gatt) { 1786 res$extd_graph <- set_graph_attr(res$extd_graph, ga, 1787 graph_attr(graph, ga)) 1788 } 1789 for (va in vatt) { 1790 notdummy <- which(!V(res$extd_graph)$dummy) 1791 res$extd_graph <- set_vertex_attr(res$extd_graph, va, 1792 notdummy, 1793 vertex_attr(graph, va)) 1794 } 1795 for (ea in eatt) { 1796 eanew <- edge_attr(graph, ea)[E(res$extd_graph)$orig] 1797 res$extd_graph <- set_edge_attr(res$extd_graph, ea, value=eanew) 1798 } 1799 } 1800 1801 res$res <- NULL 1802 res 1803} 1804 1805 1806#' @rdname layout_with_sugiyama 1807#' @param ... Passed to \code{layout_with_sugiyama}. 1808#' @export 1809 1810with_sugiyama <- function(...) layout_spec(layout_with_sugiyama, ...) 1811 1812 1813## ---------------------------------------------------------------- 1814 1815 1816#' Merging graph layouts 1817#' 1818#' Place several graphs on the same layout 1819#' 1820#' \code{merge_coords} takes a list of graphs and a list of coordinates and 1821#' places the graphs in a common layout. The method to use is chosen via the 1822#' \code{method} parameter, although right now only the \code{dla} method is 1823#' implemented. 1824#' 1825#' The \code{dla} method covers the graph with circles. Then it sorts the 1826#' graphs based on the number of vertices first and places the largest graph at 1827#' the center of the layout. Then the other graphs are placed in decreasing 1828#' order via a DLA (diffision limited aggregation) algorithm: the graph is 1829#' placed randomly on a circle far away from the center and a random walk is 1830#' conducted until the graph walks into the larger graphs already placed or 1831#' walks too far from the center of the layout. 1832#' 1833#' The \code{layout_components} function disassembles the graph first into 1834#' maximal connected components and calls the supplied \code{layout} function 1835#' for each component separately. Finally it merges the layouts via calling 1836#' \code{merge_coords}. 1837#' 1838#' @aliases layout.merge piecewise.layout 1839#' @param graphs A list of graph objects. 1840#' @param layouts A list of two-column matrices. 1841#' @param method Character constant giving the method to use. Right now only 1842#' \code{dla} is implemented. 1843#' @param layout A function object, the layout function to use. 1844#' @param \dots Additional arguments to pass to the \code{layout} layout 1845#' function. 1846#' @return A matrix with two columns and as many lines as the total number of 1847#' vertices in the graphs. 1848#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 1849#' @seealso \code{\link{plot.igraph}}, \code{\link{tkplot}}, 1850#' \code{\link{layout}}, \code{\link{disjoint_union}} 1851#' @export 1852#' @family graph layouts 1853#' @keywords graphs 1854#' @examples 1855#' 1856#' # create 20 scale-free graphs and place them in a common layout 1857#' graphs <- lapply(sample(5:20, 20, replace=TRUE), 1858#' barabasi.game, directed=FALSE) 1859#' layouts <- lapply(graphs, layout_with_kk) 1860#' lay <- merge_coords(graphs, layouts) 1861#' g <- disjoint_union(graphs) 1862#' \dontrun{plot(g, layout=lay, vertex.size=3, labels=NA, edge.color="black")} 1863 1864merge_coords <- function(graphs, layouts, method="dla") { 1865 1866 if (!all(sapply(graphs, is_igraph))) { 1867 stop("Not a graph object") 1868 } 1869 if (method == "dla") { 1870 on.exit(.Call(C_R_igraph_finalizer) ) 1871 res <- .Call(C_R_igraph_layout_merge_dla, 1872 graphs, layouts) 1873 } else { 1874 stop("Invalid `method'.") 1875 } 1876 res 1877} 1878 1879 1880 1881#' Normalize coordinates for plotting graphs 1882#' 1883#' Rescale coordinates linearly to be within given bounds. 1884#' 1885#' \code{norm_coords} normalizes a layout, it linearly transforms each 1886#' coordinate separately to fit into the given limits. 1887#' 1888#' @aliases layout.norm 1889#' @param layout A matrix with two or three columns, the layout to normalize. 1890#' @param xmin,xmax The limits for the first coordinate, if one of them or both 1891#' are \code{NULL} then no normalization is performed along this direction. 1892#' @param ymin,ymax The limits for the second coordinate, if one of them or 1893#' both are \code{NULL} then no normalization is performed along this 1894#' direction. 1895#' @param zmin,zmax The limits for the third coordinate, if one of them or both 1896#' are \code{NULL} then no normalization is performed along this direction. 1897#' @return A numeric matrix with at the same dimension as \code{layout}. 1898#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} 1899#' @export 1900#' @family graph layouts 1901#' @keywords graphs 1902 1903norm_coords <- function(layout, xmin=-1, xmax=1, ymin=-1, ymax=1, 1904 zmin=-1, zmax=1) { 1905 1906 if (!is.matrix(layout)) { 1907 stop("`layout' must be a matrix") 1908 } 1909 if (ncol(layout) != 2 && ncol(layout) != 3) { 1910 stop("`layout' should have 2 or three columns") 1911 } 1912 1913 if (!is.null(xmin) && !is.null(xmax)) { 1914 layout[,1] <- .layout.norm.col(layout[,1], xmin, xmax) 1915 } 1916 1917 if (!is.null(ymin) && !is.null(ymax)) { 1918 layout[,2] <- .layout.norm.col(layout[,2], ymin, ymax) 1919 } 1920 1921 if (ncol(layout)==3 && !is.null(zmin) && !is.null(zmax)) { 1922 layout[,3] <- .layout.norm.col(layout[,3], zmin, zmax) 1923 } 1924 1925 layout 1926} 1927 1928.layout.norm.col <- function(v, min, max) { 1929 1930 vr <- range(v) 1931 if (vr[1]==vr[2]) { 1932 fac <- 1 1933 } else { 1934 fac <- (max-min)/(vr[2]-vr[1]) 1935 } 1936 1937 (v-vr[1]) * fac + min 1938} 1939 1940#' @rdname merge_coords 1941#' @aliases piecewise.layout 1942#' @param graph The input graph. 1943#' @export 1944 1945layout_components <- function(graph, layout=layout_with_kk, ...) { 1946 1947 if (!is_igraph(graph)) { 1948 stop("Not a graph object") 1949 } 1950 1951 V(graph)$id <- seq(vcount(graph)) 1952 gl <- decompose(graph) 1953 ll <- lapply(gl, layout, ...) 1954 1955 l <- merge_coords(gl, ll) 1956 l[ unlist(sapply(gl, vertex_attr, "id")), ] <- l[] 1957 l 1958} 1959 1960#' Spring layout, this was removed from igraph 1961#' 1962#' Now it calls the Fruchterman-Reingold layout, with a warning. 1963#' 1964#' @param graph Input graph. 1965#' @param ... Extra arguments are ignored. 1966#' @return Layout coordinates, a two column matrix. 1967#' 1968#' @export 1969 1970layout.spring <- function(graph, ...) { 1971 warning("Spring layout was removed, we use Fruchterman-Reingold instead.") 1972 layout_with_fr(graph) 1973} 1974 1975#' SVD layout, this was removed from igraph 1976#' 1977#' Now it calls the Fruchterman-Reingold layout, with a warning. 1978#' 1979#' @param graph Input graph. 1980#' @param ... Extra arguments are ignored. 1981#' @return Layout coordinates, a two column matrix. 1982#' 1983#' @export 1984 1985layout.svd <- function(graph, ...) { 1986 warning("SVD layout was removed, we use Fruchterman-Reingold instead.") 1987 layout_with_fr(graph) 1988} 1989 1990#' Grid Fruchterman-Reingold layout, this was removed from igraph 1991#' 1992#' Now it calls the Fruchterman-Reingold layout, with a warning. 1993#' 1994#' @param graph Input graph. 1995#' @param ... Extra arguments are ignored. 1996#' @return Layout coordinates, a two column matrix. 1997#' 1998#' @export 1999 2000layout.fruchterman.reingold.grid <- function(graph, ...) { 2001 warning("Grid Fruchterman-Reingold layout was removed,\n", 2002 "we use Fruchterman-Reingold instead.") 2003 layout_with_fr(graph) 2004} 2005