#'   Merging graph layouts
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `piecewise.layout()` was renamed to [layout_components()] to create a more
#' consistent API.
#' @inheritParams layout_components
#' @keywords internal
#' @export
piecewise.layout <- function(graph, layout = layout_with_kk, ...) {
  # nocov start
  lifecycle::deprecate_soft(
    "2.0.0",
    "piecewise.layout()",
    "layout_components()"
  )
  layout_components(graph = graph, layout = layout, ...)
} # nocov end

#' The Sugiyama graph layout generator
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.sugiyama()` was renamed to [layout_with_sugiyama()] to create a more
#' consistent API.
#' @inheritParams layout_with_sugiyama
#' @keywords internal
#' @export
layout.sugiyama <- function(
  graph,
  layers = NULL,
  hgap = 1,
  vgap = 1,
  maxiter = 100,
  weights = NULL,
  attributes = c("default", "all", "none")
) {
  # nocov start
  lifecycle::deprecate_soft(
    "2.0.0",
    "layout.sugiyama()",
    "layout_with_sugiyama()"
  )
  layout_with_sugiyama(
    graph = graph,
    layers = layers,
    hgap = hgap,
    vgap = vgap,
    maxiter = maxiter,
    weights = weights,
    attributes = attributes
  )
} # nocov end

#' Generate coordinates to place the vertices of a graph in a star-shape
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.star()` was renamed to [layout_as_star()] to create a more
#' consistent API.
#' @inheritParams layout_as_star
#' @keywords internal
#' @export
layout.star <- function(graph, center = V(graph)[1], order = NULL) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.star()", "layout_as_star()")
  layout_as_star(graph = graph, center = center, order = order)
} # nocov end

#' Normalize coordinates for plotting graphs
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.norm()` was renamed to [norm_coords()] to create a more
#' consistent API.
#' @inheritParams norm_coords
#' @keywords internal
#' @export
layout.norm <- function(
  layout,
  xmin = -1,
  xmax = 1,
  ymin = -1,
  ymax = 1,
  zmin = -1,
  zmax = 1
) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.norm()", "norm_coords()")
  norm_coords(
    layout = layout,
    xmin = xmin,
    xmax = xmax,
    ymin = ymin,
    ymax = ymax,
    zmin = zmin,
    zmax = zmax
  )
} # nocov end

#' Merging graph layouts
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.merge()` was renamed to [merge_coords()] to create a more
#' consistent API.
#' @inheritParams merge_coords
#' @keywords internal
#' @export
layout.merge <- function(graphs, layouts, method = "dla") {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.merge()", "merge_coords()")
  merge_coords(graphs = graphs, layouts = layouts, method = method)
} # nocov end

#' Graph layout by multidimensional scaling
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.mds()` was renamed to [layout_with_mds()] to create a more
#' consistent API.
#' @inheritParams layout_with_mds
#' @keywords internal
#' @export
layout.mds <- function(
  graph,
  dist = NULL,
  dim = 2,
  options = arpack_defaults()
) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.mds()", "layout_with_mds()")
  layout_with_mds(graph = graph, dist = dist, dim = dim, options = options)
} # nocov end

#' Simple grid layout
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.grid()` was renamed to [layout_on_grid()] to create a more
#' consistent API.
#' @inheritParams layout_on_grid
#' @keywords internal
#' @export
layout.grid <- function(graph, width = 0, height = 0, dim = 2) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.grid()", "layout_on_grid()")
  layout_on_grid(graph = graph, width = width, height = height, dim = dim)
} # nocov end

#' The graphopt layout algorithm
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.graphopt()` was renamed to [layout_with_graphopt()] to create a more
#' consistent API.
#' @inheritParams layout_with_graphopt
#' @keywords internal
#' @export
layout.graphopt <- function(
  graph,
  start = NULL,
  niter = 500,
  charge = 0.001,
  mass = 30,
  spring.length = 0,
  spring.constant = 1,
  max.sa.movement = 5
) {
  # nocov start
  lifecycle::deprecate_soft(
    "2.0.0",
    "layout.graphopt()",
    "layout_with_graphopt()"
  )
  layout_with_graphopt(
    graph = graph,
    start = start,
    niter = niter,
    charge = charge,
    mass = mass,
    spring.length = spring.length,
    spring.constant = spring.constant,
    max.sa.movement = max.sa.movement
  )
} # nocov end

#' The GEM layout algorithm
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.gem()` was renamed to [layout_with_gem()] to create a more
#' consistent API.
#' @inheritParams layout_with_gem
#' @keywords internal
#' @export
layout.gem <- function(
  graph,
  coords = NULL,
  maxiter = 40 * vcount(graph)^2,
  temp.max = max(vcount(graph), 1),
  temp.min = 1 / 10,
  temp.init = sqrt(max(vcount(graph), 1))
) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.gem()", "layout_with_gem()")
  layout_with_gem(
    graph = graph,
    coords = coords,
    maxiter = maxiter,
    temp.max = temp.max,
    temp.min = temp.min,
    temp.init = temp.init
  )
} # nocov end

#' The Davidson-Harel layout algorithm
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.davidson.harel()` was renamed to [layout_with_dh()] to create a more
#' consistent API.
#' @inheritParams layout_with_dh
#' @keywords internal
#' @export
layout.davidson.harel <- function(
  graph,
  coords = NULL,
  maxiter = 10,
  fineiter = max(10, log2(vcount(graph))),
  cool.fact = 0.75,
  weight.node.dist = 1.0,
  weight.border = 0.0,
  weight.edge.lengths = edge_density(graph) / 10,
  weight.edge.crossings = 1.0 - sqrt(edge_density(graph)),
  weight.node.edge.dist = 0.2 * (1 - edge_density(graph))
) {
  # nocov start
  lifecycle::deprecate_soft(
    "2.0.0",
    "layout.davidson.harel()",
    "layout_with_dh()"
  )
  layout_with_dh(
    graph = graph,
    coords = coords,
    maxiter = maxiter,
    fineiter = fineiter,
    cool.fact = cool.fact,
    weight.node.dist = weight.node.dist,
    weight.border = weight.border,
    weight.edge.lengths = weight.edge.lengths,
    weight.edge.crossings = weight.edge.crossings,
    weight.node.edge.dist = weight.node.edge.dist
  )
} # nocov end

#' Simple two-row layout for bipartite graphs
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.bipartite()` was renamed to [layout_as_bipartite()] to create a more
#' consistent API.
#' @inheritParams layout_as_bipartite
#' @keywords internal
#' @export
layout.bipartite <- function(
  graph,
  types = NULL,
  hgap = 1,
  vgap = 1,
  maxiter = 100
) {
  # nocov start
  lifecycle::deprecate_soft(
    "2.0.0",
    "layout.bipartite()",
    "layout_as_bipartite()"
  )
  layout_as_bipartite(
    graph = graph,
    types = types,
    hgap = hgap,
    vgap = vgap,
    maxiter = maxiter
  )
} # nocov end

#' Choose an appropriate graph layout algorithm automatically
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.auto()` was renamed to [layout_nicely()] to create a more
#' consistent API.
#' @inheritParams layout_nicely
#' @keywords internal
#' @export
layout.auto <- function(graph, dim = 2, ...) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.auto()", "layout_nicely()")
  layout_nicely(graph = graph, dim = dim, ...)
} # nocov end

## ----------------------------------------------------------------
##
##   IGraph R package
##   Copyright (C) 2003-2014  Gabor Csardi <csardi.gabor@gmail.com>
##   334 Harvard street, Cambridge, MA 02139 USA
##
##   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
##
## ----------------------------------------------------------------

## ----------------------------------------------------------------
## This is the new layout API
## ----------------------------------------------------------------

#' Graph layouts
#'
#' This is a generic function to apply a layout function to
#' a graph.
#'
#' There are two ways to calculate graph layouts in igraph.
#' The first way is to call a layout function (they all have
#' prefix `layout_()` on a graph, to get the vertex coordinates.
#'
#' The second way (new in igraph 0.8.0), has two steps, and it
#' is more flexible. First you call a layout specification
#' function (the one without the `layout_()` prefix, and
#' then `layout_()` (or [add_layout_()]) to
#' perform the layouting.
#'
#' The second way is preferred, as it is more flexible. It allows
#' operations before and after the layouting. E.g. using the
#' `component_wise()` argument, the layout can be calculated
#' separately for each component, and then merged to get the
#' final results.
#'
#' @aliases layout
#' @section Modifiers:
#' Modifiers modify how a layout calculation is performed.
#' Modifiers are applied in the order they are specified as arguments to
#' `layout_()`.
#'
#' There are two types of modifiers:
#' \itemize{
#'   \item **Pre-layout modifiers** affect how the layout is calculated.
#'     Only one pre-layout modifier can be used at a time.
#'   \item **Post-layout modifiers** transform the resulting coordinates.
#'     Multiple post-layout modifiers can be chained together.
#' }
#'
#' Currently implemented modifiers:
#' \itemize{
#'   \item `component_wise()` (pre-layout) calculates the layout separately
#'     for each component of the graph, and then merges them.
#'   \item `normalize()` (post-layout) scales the layout to a square.
#' }
#'
#' Custom modifiers can be created using the `layout_modifier()` function.
#' A custom modifier must specify:
#' \itemize{
#'   \item `id`: A unique identifier string for the modifier
#'   \item `type`: Either `"pre"` for pre-layout or `"post"` for post-layout
#'   \item `args`: A list of arguments to pass to the apply function
#'   \item `apply`: A function with signature
#'     `function(graph, layout, modifier_args)` that performs the modification.
#'     For pre-layout modifiers, `layout` is the layout specification.
#'     For post-layout modifiers, `layout` is the coordinate matrix to transform.
#' }
#'
#' @param graph The input graph.
#' @param layout The layout specification. It must be a call
#'   to a layout specification function.
#' @param ... Further modifiers, see a complete list below.
#'   For the [print()] methods, it is ignored.
#' @return The return value of the layout function, usually a
#'   two column matrix. For 3D layouts a three column matrix.
#'
#' @seealso [add_layout_()] to add the layout to the
#'   graph as an attribute.
#' @export
#' @family graph layouts
#' @examples
#' g <- make_ring(10) + make_full_graph(5)
#' coords <- layout_(g, as_star())
#' plot(g, layout = coords)
#'
#' # Using modifiers
#' g <- make_ring(10) + make_ring(5)
#' coords <- layout_(g, in_circle(), component_wise(), normalize())
#' plot(g, layout = coords)
#'
#' # Creating a custom post-layout modifier
#' scale_by <- function(factor) {
#'   layout_modifier(
#'     id = "scale_by",
#'     type = "post",
#'     args = list(factor = factor),
#'     apply = function(graph, layout, modifier_args) {
#'       layout * modifier_args$factor
#'     }
#'   )
#' }
#' coords <- layout_(make_ring(10), in_circle(), scale_by(3))
#' plot(make_ring(10), layout = coords)
layout_ <- function(graph, layout, ...) {
  modifiers <- list(...)
  stopifnot(all(sapply(modifiers, inherits, what = "igraph_layout_modifier")))

  ids <- sapply(modifiers, "[[", "id")
  if (anyDuplicated(ids)) {
    cli::cli_abort("Duplicate modifiers.")
  }
  names(modifiers) <- ids

  # Separate modifiers by type
  is_pre <- vapply(modifiers, function(m) isTRUE(m$type == "pre"), logical(1))
  pre_modifiers <- modifiers[is_pre]
  post_modifiers <- modifiers[!is_pre]

  # Apply pre-layout modifiers
  if (length(pre_modifiers) > 0) {
    # Enforce single pre-layout modifier restriction
    if (length(pre_modifiers) > 1) {
      cli::cli_abort("Multiple pre-layout modifiers are not supported.")
    }
    modifier <- pre_modifiers[[1]]
    result <- modifier$apply(graph, layout, modifier$args)
  } else {
    # No pre-layout modifiers, do standard layout
    result <- do_call(layout$fun, list(graph = graph), layout$args)
  }

  # Apply post-layout modifiers in order
  for (modifier in post_modifiers) {
    result <- modifier$apply(graph, result, modifier$args)
  }

  result
}


#' Add layout to graph
#'
#' @param graph The input graph.
#' @param ... Additional arguments are passed to [layout_()].
#' @param overwrite Whether to overwrite the layout of the graph,
#'    if it already has one.
#' @return The input graph, with the layout added.
#'
#' @seealso [layout_()] for a description of the layout API.
#' @export
#' @family graph layouts
#' @examples
#' (make_star(11) + make_star(11)) %>%
#'   add_layout_(as_star(), component_wise()) %>%
#'   plot()
add_layout_ <- function(graph, ..., overwrite = TRUE) {
  if (overwrite && "layout" %in% graph_attr_names(graph)) {
    graph <- delete_graph_attr(graph, "layout")
  }
  graph$layout <- layout_(graph, ...)
  graph
}


layout_spec <- function(fun, ...) {
  my_call <- match.call(sys.function(1), sys.call(1))
  my_call[[1]] <- substitute(fun)
  structure(
    list(
      fun = fun,
      call_str = sub("(", "(<graph>, ", deparse(my_call), fixed = TRUE),
      args = list(...)
    ),
    class = "igraph_layout_spec"
  )
}


#' @rdname layout_
#' @param x The layout specification
#' @method print igraph_layout_spec
#' @export
print.igraph_layout_spec <- function(x, ...) {
  cat(paste(
    sep = "",
    "igraph layout specification, see ?layout_:\n",
    x$call_str,
    "\n"
  ))
}


#' Create a layout modifier
#'
#' This is a constructor function for creating custom layout modifiers.
#' Layout modifiers can be used with [layout_()] to modify how layouts
#' are calculated or to transform the resulting coordinates.
#'
#' @param ... Named arguments that define the modifier. Must include:
#'   \describe{
#'     \item{id}{A unique identifier string for the modifier}
#'     \item{type}{Either `"pre"` for pre-layout or `"post"` for post-layout}
#'     \item{args}{A list of arguments to pass to the apply function}
#'     \item{apply}{A function with signature
#'       `function(graph, layout, modifier_args)` that performs the modification}
#'   }
#'
#' @return An object of class `igraph_layout_modifier`.
#'
#' @seealso [layout_()] for using modifiers, [component_wise()], [normalize()]
#'   for examples of built-in modifiers.
#'
#' @family layout modifiers
#' @export
#' @examples
#' # Create a custom post-layout modifier that scales coordinates
#' scale_by <- function(factor) {
#'   layout_modifier(
#'     id = "scale_by",
#'     type = "post",
#'     args = list(factor = factor),
#'     apply = function(graph, layout, modifier_args) {
#'       layout * modifier_args$factor
#'     }
#'   )
#' }
#'
#' # Use the custom modifier
#' g <- make_ring(10)
#' coords <- layout_(g, in_circle(), scale_by(2))
#' plot(g, layout = coords)
layout_modifier <- function(...) {
  structure(
    list(...),
    class = "igraph_layout_modifier"
  )
}


#' @rdname layout_
#' @method print igraph_layout_modifier
#' @export
print.igraph_layout_modifier <- function(x, ...) {
  cat(sep = "", "igraph layout modifier: ", x$id, ".\n")
}

#' Component-wise layout
#'
#' This is a layout modifier function, and it can be used
#' to calculate the layout separately for each component
#' of the graph.
#'
#' @param merge_method Merging algorithm, the `method`
#'   argument of [merge_coords()].
#'
#' @family layout modifiers
#' @family graph layouts
#' @seealso [merge_coords()], [layout_()].
#' @export
#' @examples
#' g <- make_ring(10) + make_ring(10)
#' g %>%
#'   add_layout_(in_circle(), component_wise()) %>%
#'   plot()
component_wise <- function(merge_method = "dla") {
  args <- grab_args()

  layout_modifier(
    id = "component_wise",
    type = "pre",
    args = args,
    apply = function(graph, layout, modifier_args) {
      graph$id <- seq(vcount(graph))
      comps <- decompose(graph)
      coords <- lapply(comps, function(comp) {
        do_call(layout$fun, list(graph = comp), layout$args)
      })
      all_coords <- merge_coords(
        comps,
        coords,
        method = modifier_args$merge_method
      )
      all_coords[unlist(sapply(comps, vertex_attr, "id")), ] <- all_coords[]
      all_coords
    }
  )
}

#' Normalize layout
#'
#' Scale coordinates of a layout.
#'
#' @param xmin,xmax Minimum and maximum for x coordinates.
#' @param ymin,ymax Minimum and maximum for y coordinates.
#' @param zmin,zmax Minimum and maximum for z coordinates.
#'
#' @family layout modifiers
#' @family graph layouts
#' @seealso [merge_coords()], [layout_()].
#' @export
#' @examples
#' layout_(make_ring(10), with_fr(), normalize())
normalize <- function(
  xmin = -1,
  xmax = 1,
  ymin = xmin,
  ymax = xmax,
  zmin = xmin,
  zmax = xmax
) {
  args <- grab_args()

  layout_modifier(
    id = "normalize",
    type = "post",
    args = args,
    apply = function(graph, layout, modifier_args) {
      do_call(
        norm_coords,
        list(layout),
        modifier_args
      )
    }
  )
}

## ----------------------------------------------------------------
## Layout definitions for the new API
## ----------------------------------------------------------------

#' Simple two-row layout for bipartite graphs
#'
#' Minimize edge-crossings in a simple two-row (or column) layout for bipartite
#' graphs.
#'
#' The layout is created by first placing the vertices in two rows, according
#' to their types. Then the positions within the rows are optimized to minimize
#' edge crossings, using the Sugiyama algorithm (see
#' [layout_with_sugiyama()]).
#'
#' @param graph The bipartite input graph. It should have a logical
#'   \sQuote{`type`} vertex attribute, or the `types` argument must be
#'   given.
#' @param types A logical vector, the vertex types. If this argument is
#'   `NULL` (the default), then the \sQuote{`type`} vertex attribute is
#'   used.
#' @param hgap Real scalar, the minimum horizontal gap between vertices in the
#'   same layer.
#' @param vgap Real scalar, the distance between the two layers.
#' @param maxiter Integer scalar, the maximum number of iterations in the
#'   crossing minimization stage. 100 is a reasonable default; if you feel that
#'   you have too many edge crossings, increase this.
#' @return A matrix with two columns and as many rows as the number of vertices
#'   in the input graph.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout_with_sugiyama()]
#' @keywords graphs
#' @export
#' @family graph layouts
#' @examples
#' # Random bipartite graph
#' inc <- matrix(sample(0:1, 50, replace = TRUE, prob = c(2, 1)), 10, 5)
#' g <- graph_from_biadjacency_matrix(inc)
#' plot(g,
#'   layout = layout_as_bipartite,
#'   vertex.color = c("green", "cyan")[V(g)$type + 1]
#' )
#'
#' # Two columns
#' g %>%
#'   add_layout_(as_bipartite()) %>%
#'   plot()
layout_as_bipartite <- function(
  graph,
  types = NULL,
  hgap = 1,
  vgap = 1,
  maxiter = 100
) {
  ## Argument checks
  ensure_igraph(graph)
  # Use the _impl function
  layout_bipartite_impl(
    graph = graph,
    types = types,
    hgap = hgap,
    vgap = vgap,
    maxiter = maxiter
  )
}


#' @rdname layout_as_bipartite
#' @param ... Arguments to pass to `layout_as_bipartite()`.
#' @export
as_bipartite <- function(...) layout_spec(layout_as_bipartite, ...)


## ----------------------------------------------------------------

#' Generate coordinates to place the vertices of a graph in a star-shape
#'
#' A simple layout generator, that places one vertex in the center of a circle
#' and the rest of the vertices equidistantly on the perimeter.
#'
#' It is possible to choose the vertex that will be in the center, and the
#' order of the vertices can be also given.
#'
#' @param graph The graph to layout.
#' @param center The id of the vertex to put in the center. By default it is
#'   the first vertex.
#' @param order Numeric vector, the order of the vertices along the perimeter.
#'   The default ordering is given by the vertex ids.
#' @return A matrix with two columns and as many rows as the number of vertices
#'   in the input graph.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout()] and [layout_with_drl()] for other layout
#' algorithms, [plot.igraph()] and [tkplot()] on how to
#' plot graphs and [star()] on how to create ring graphs.
#' @keywords graphs
#' @export
#' @family graph layouts
#' @examples
#'
#' g <- make_star(10)
#' layout_as_star(g)
#'
#' ## Alternative form
#' layout_(g, as_star())
layout_as_star <- function(graph, center = V(graph)[1], order = NULL) {
  # Argument checks
  ensure_igraph(graph)
  if (vcount(graph) == 0) {
    # Any other layout will do so just pick one that supports graphs with no
    # vertices
    return(layout_in_circle(graph))
  }
  # Use the _impl function
  layout_star_impl(
    graph = graph,
    center = center,
    order = order
  )
}


#' @rdname layout_as_star
#' @param ... Arguments to pass to `layout_as_star()`.
#' @export
as_star <- function(...) layout_spec(layout_as_star, ...)


## ----------------------------------------------------------------

#' The Reingold-Tilford graph layout algorithm
#'
#' A tree-like layout, it is perfect for trees, acceptable for graphs with not
#' too many cycles.
#'
#' 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, the reference below.
#'
#' If the given graph is not a tree, a breadth-first search is executed first
#' to obtain a possible spanning tree.
#'
#' @param graph The input graph.
#' @param root The index of the root vertex or root vertices.  If this is a
#'   non-empty vector then the supplied vertex ids are used as the roots of the
#'   trees (or a single tree if the graph is connected).  If it is an empty
#'   vector, then the root vertices are automatically calculated based on
#'   topological sorting, performed with the opposite mode than the `mode`
#'   argument. After the vertices have been sorted, one is selected from each
#'   component.
#' @param circular Logical scalar, whether to plot the tree in a circular
#'   fashion. Defaults to `FALSE`, so the tree branches are going bottom-up
#'   (or top-down, see the `flip.y` argument.
#' @param rootlevel This argument can be useful when drawing forests which are
#'   not trees (i.e. they are unconnected and have tree components). It specifies
#'   the level of the root vertices for every tree in the forest. It is only
#'   considered if the `roots` argument is not an empty vector.
#' @param mode Specifies which edges to consider when building the tree.  If it
#'   is \sQuote{out}, then only the outgoing, if it is \sQuote{in}, then only the
#'   incoming edges of a parent are considered. If it is \sQuote{all} then all
#'   edges are used (this was the behavior in igraph 0.5 and before). This
#'   parameter also influences how the root vertices are calculated, if they are
#'   not given. See the `roots` parameter.
#' @param flip.y Logical scalar, whether to flip the \sQuote{y} coordinates.
#'   The default is flipping because that puts the root vertex on the top.
#' @return A numeric matrix with two columns, and one row for each vertex.
#' @author Tamas Nepusz \email{ntamas@@gmail.com} and Gabor Csardi
#' \email{csardi.gabor@@gmail.com}
#' @references Reingold, E and Tilford, J (1981). Tidier drawing of trees.
#' *IEEE Trans. on Softw. Eng.*, SE-7(2):223--228.
#' @keywords graphs
#' @export
#' @family graph layouts
#' @examples
#'
#' tree <- make_tree(20, 3)
#' plot(tree, layout = layout_as_tree)
#' plot(tree, layout = layout_as_tree(tree, flip.y = FALSE))
#' plot(tree, layout = layout_as_tree(tree, circular = TRUE))
#'
#' tree2 <- make_tree(10, 3) + make_tree(10, 2)
#' plot(tree2, layout = layout_as_tree)
#' plot(tree2, layout = layout_as_tree(tree2,
#'   root = c(1, 11),
#'   rootlevel = c(2, 1)
#' ))
layout_as_tree <- function(
  graph,
  root = numeric(),
  circular = FALSE,
  rootlevel = numeric(),
  mode = c("out", "in", "all"),
  flip.y = TRUE
) {
  ensure_igraph(graph)
  root <- as_igraph_vs(graph, root) - 1
  circular <- as.logical(circular)
  rootlevel <- as.double(rootlevel)
  mode <- switch(
    igraph_match_arg(mode),
    "out" = 1,
    "in" = 2,
    "all" = 3,
    "total" = 3
  )
  flip.y <- as.logical(flip.y)

  on.exit(.Call(Rx_igraph_finalizer))
  res <- .Call(
    Rx_igraph_layout_reingold_tilford,
    graph,
    root,
    mode,
    rootlevel,
    circular
  )
  if (flip.y && vcount(graph) > 0) {
    res[, 2] <- max(res[, 2]) - res[, 2]
  }
  res
}


#' @rdname layout_as_tree
#' @param ... Passed to `layout_as_tree()`.
#' @export
as_tree <- function(...) layout_spec(layout_as_tree, ...)


#' The Reingold-Tilford graph layout algorithm
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.reingold.tilford()` was renamed to [layout_as_tree()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.reingold.tilford <- function(..., params = list()) {
  lifecycle::deprecate_soft(
    "2.1.0",
    "layout.reingold.tilford()",
    "layout_as_tree()"
  )
  do_call(layout_as_tree, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' Graph layout with vertices on a circle.
#'
#' Place vertices on a circle, in the order of their vertex ids.
#'
#' If you want to order the vertices differently, then permute them using the
#' [permute()] function.
#'
#' @param graph The input graph.
#' @param order The vertices to place on the circle, in the order of their
#'   desired placement. Vertices that are not included here will be placed at
#'   (0,0).
#' @return A numeric matrix with two columns, and one row for each vertex.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @keywords graphs
#' @export
#' @family graph layouts
#' @examplesIf igraph:::has_glpk() && rlang::is_installed("igraphdata")
#'
#' ## Place vertices on a circle, order them according to their
#' ## community
#' library(igraphdata)
#' data(karate)
#' karate_groups <- cluster_optimal(karate)
#' coords <- layout_in_circle(karate,
#'   order =
#'     order(membership(karate_groups))
#' )
#' V(karate)$label <- sub("Actor ", "", V(karate)$name)
#' V(karate)$label.color <- membership(karate_groups)
#' V(karate)$shape <- "none"
#' plot(karate, layout = coords)
layout_in_circle <- function(graph, order = V(graph)) {
  # Use the _impl function
  layout_circle_impl(
    graph = graph,
    order = order
  )
}

#' @rdname layout_in_circle
#' @param ... Passed to `layout_in_circle()`.
#' @export
in_circle <- function(...) layout_spec(layout_in_circle, ...)

#' Graph layout with vertices on a circle
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.circle()` was renamed to [layout_in_circle()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.circle <- function(..., params = list()) {
  lifecycle::deprecate_soft("2.1.0", "layout.circle()", "layout_in_circle()")
  do_call(layout_in_circle, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' Choose an appropriate graph layout algorithm automatically
#'
#' This function tries to choose an appropriate graph layout algorithm for the
#' graph, automatically, based on a simple algorithm. See details below.
#'
#' `layout_nicely()` tries to choose an appropriate layout function for the
#' supplied graph, and uses that to generate the layout. The current
#' implementation works like this:
#' \enumerate{
#' \item If the graph has a graph attribute called \sQuote{layout},
#' then this is used. If this attribute is an R function, then it is called, with the graph and any other extra arguments.
#'
#' \item Otherwise, if the graph has vertex attributes called \sQuote{x} and
#' \sQuote{y}, then these are used as coordinates. If the graph has an
#' additional \sQuote{z} vertex attribute, that is also used.
#'
#' \item Otherwise, if the graph is a forest and has less than 30 vertices, `layout_as_tree()` is used.
#'
#' \item Otherwise,
#' if the graph is connected and has less than 1000 vertices, the
#' Fruchterman-Reingold layout is used, by calling `layout_with_fr()`.
#' \item Otherwise the DrL layout is used, `layout_with_drl()` is called.  }
#'
#' In layout algorithm implementations, an argument named \sQuote{weights} is
#' typically used to specify the weights of the edges if the layout algorithm
#' supports them. In this case, omitting \sQuote{weights} or setting it to
#' `NULL` will make igraph use the 'weight' edge attribute from the graph
#' if it is present. However, most layout algorithms do not support non-positive
#' weights, so `layout_nicely()` would fail if you simply called it on
#' your graph without specifying explicit weights and the weights happened to
#' include non-positive numbers. We strive to ensure that `layout_nicely()`
#' works out-of-the-box for most graphs, so the rule is that if you omit
#' \sQuote{weights} or set it to `NULL` and `layout_nicely()` would
#' end up calling `layout_with_fr()` or `layout_with_drl()`, we do not
#' forward the weights to these functions and issue a warning about this. You
#' can use `weights = NA` to silence the warning.
#'
#' @param graph The input graph
#' @param dim Dimensions, should be 2 or 3.
#' @param \dots For `layout_nicely()` the extra arguments are passed to
#'   the real layout function. For `nicely()` all argument are passed to
#'   `layout_nicely()`.
#' @return A numeric matrix with two or three columns.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [plot.igraph()]
#' @keywords graphs
#' @export
#' @family graph layouts
layout_nicely <- function(graph, dim = 2, ...) {
  ## 1. If there is a 'layout' graph attribute, we just use that.
  ## 2. Otherwise, if there are vertex attributes called 'x' and 'y',
  ##    we use those (and the 'z' vertex attribute as well, if present).
  ## 3. If the graph is a forest and has less than 30 vertices, layout_as_tree is used
  ## 3. Otherwise, if the graph is small (<1000) we use
  ##    the Fruchterman-Reingold layout.
  ## 4. Otherwise we use the DrL layout generator.

  if ("layout" %in% graph_attr_names(graph)) {
    lay <- graph_attr(graph, "layout")
    if (is.function(lay)) {
      if (!identical(lay, layout_nicely)) {
        return(lay(graph, ...))
      } else {
        # nop, we'll deal with it later below
      }
    } else {
      return(lay)
    }
  }

  if (all(c("x", "y") %in% vertex_attr_names(graph))) {
    if ("z" %in% vertex_attr_names(graph)) {
      cbind(V(graph)$x, V(graph)$y, V(graph)$z)
    } else {
      cbind(V(graph)$x, V(graph)$y)
    }
  } else {
    args <- list(...)
    if (!("weights" %in% names(args)) || is.null(args$weights)) {
      if ("weight" %in% edge_attr_names(graph)) {
        weights <- E(graph)$weight
        if (any(weights <= 0, na.rm = TRUE)) {
          cli::cli_warn(
            "Non-positive edge weight found, ignoring all weights during graph layout."
          )
          args$weights <- NA
        } else {
          args$weights <- weights
        }
      }
    }

    args$graph <- graph

    if (is_forest(graph) && vcount(graph) <= 30 && is.null(args$weights)) {
      return(do.call(layout_as_tree, args))
    }

    args$dim <- dim

    if (vcount(graph) < 1000) {
      layout <- do.call(layout_with_fr, args)
      align_layout(graph, layout)
    } else {
      do.call(layout_with_drl, args)
    }
  }
}


#' @rdname layout_nicely
#' @export
nicely <- function(...) layout_spec(layout_nicely, ...)


## ----------------------------------------------------------------

#' Simple grid layout
#'
#' This layout places vertices on a rectangular grid, in two or three
#' dimensions.
#'
#' The function places the vertices on a simple rectangular grid, one after the
#' other. If you want to change the order of the vertices, then see the
#' [permute()] function.
#'
#' @param graph The input graph.
#' @param width The number of vertices in a single row of the grid. If this is
#'   zero or negative, then for 2d layouts the width of the grid will be the
#'   square root of the number of vertices in the graph, rounded up to the next
#'   integer. Similarly, it will be the cube root for 3d layouts.
#' @param height The number of vertices in a single column of the grid, for
#'   three dimensional layouts. If this is zero or negative, then it is
#'   determinted automatically.
#' @param dim Two or three. Whether to make 2d or a 3d layout.
#' @return A two-column or three-column matrix.
#' @author Tamas Nepusz \email{ntamas@@gmail.com}
#' @seealso [layout()] for other layout generators
#' @keywords graphs
#' @export
#' @family graph layouts
#' @examples
#'
#' g <- make_lattice(c(3, 3))
#' layout_on_grid(g)
#'
#' g2 <- make_lattice(c(3, 3, 3))
#' layout_on_grid(g2, dim = 3)
#'
#' plot(g, layout = layout_on_grid)
#' if (interactive() && requireNamespace("rgl", quietly = TRUE)) {
#'   rglplot(g, layout = layout_on_grid(g, dim = 3))
#' }
layout_on_grid <- function(graph, width = 0, height = 0, dim = 2) {
  # Argument checks
  ensure_igraph(graph)
  width <- as.numeric(width)
  dim <- as.numeric(dim)
  stopifnot(dim == 2 || dim == 3)
  if (dim == 3) {
    height <- as.numeric(height)
  }

  # Function call
  if (dim == 2) {
    layout_grid_impl(
      graph = graph,
      width = width
    )
  } else {
    layout_grid_3d_impl(
      graph = graph,
      width = width,
      height = height
    )
  }
}


#' @rdname layout_on_grid
#' @param ... Passed to `layout_on_grid()`.
#' @export
on_grid <- function(...) layout_spec(layout_on_grid, ...)


#' Simple grid layout
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' Use [layout_on_grid()].
#' @inheritParams layout_on_grid
#' @export
#' @export
#' @keywords internal
layout.grid.3d <- function(graph, width = 0, height = 0) {
  lifecycle::deprecate_stop("2.1.0", "layout.grid.3d()", "layout_on_grid()")
}

## ----------------------------------------------------------------

#' Graph layout with vertices on the surface of a sphere
#'
#' Place vertices on a sphere, approximately uniformly, in the order of their
#' vertex ids.
#'
#' `layout_on_sphere()` places the vertices (approximately) uniformly on the
#' surface of a sphere, this is thus a 3d layout. It is not clear however what
#' \dQuote{uniformly on a sphere} means.
#'
#' If you want to order the vertices differently, then permute them using the
#' [permute()] function.
#'
#' @param graph The input graph.
#' @return A numeric matrix with three columns, and one row for each vertex.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @keywords graphs
#' @export
#' @family graph layouts
layout_on_sphere <- function(graph) {
  # Use the _impl function
  layout_sphere_impl(
    graph = graph
  )
}


#' @rdname layout_on_sphere
#' @param ... Passed to `layout_on_sphere()`.
#' @export
on_sphere <- function(...) layout_spec(layout_on_sphere, ...)

#' Graph layout with vertices on the surface of a sphere
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.sphere()` was renamed to [layout_on_sphere()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.sphere <- function(..., params = list()) {
  lifecycle::deprecate_soft("2.1.0", "layout.sphere()", "layout_on_sphere()")
  do_call(layout_on_sphere, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' Randomly place vertices on a plane or in 3d space
#'
#' This function uniformly randomly places the vertices of the graph in two or
#' three dimensions.
#'
#' Randomly places vertices on a \[-1,1\] square (in 2d) or in a cube (in 3d). It
#' is probably a useless layout, but it can use as a starting point for other
#' layout generators.
#'
#' @param graph The input graph.
#' @param dim Integer scalar, the dimension of the space to use. It must be 2
#'   or 3.
#' @return A numeric matrix with two or three columns.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @keywords graphs
#' @export
#' @family graph layouts
layout_randomly <- function(graph, dim = c(2, 3)) {
  ensure_igraph(graph)

  dim <- igraph_match_arg(dim)

  if (dim == 2) {
    layout_random_impl(
      graph = graph
    )
  } else if (dim == 3) {
    layout_random_3d_impl(
      graph = graph
    )
  }
}

#' @rdname layout_randomly
#' @param ... Parameters to pass to `layout_randomly()`.
#' @export
randomly <- function(...) layout_spec(layout_randomly, ...)

#' Randomly place vertices on a plane or in 3d space
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.random()` was renamed to [layout_randomly()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.random <- function(..., params = list()) {
  lifecycle::deprecate_soft("2.1.0", "layout.random()", "layout_randomly()")
  do_call(layout_randomly, .args = c(list(...), params))
}


## ----------------------------------------------------------------

#' The Davidson-Harel layout algorithm
#'
#' Place vertices of a graph on the plane, according to the simulated annealing
#' algorithm by Davidson and Harel.
#'
#' This function implements the algorithm by Davidson and Harel, see Ron
#' Davidson, David Harel: Drawing Graphs Nicely Using Simulated Annealing. ACM
#' Transactions on Graphics 15(4), pp. 301-331, 1996.
#'
#' The algorithm uses simulated annealing and a sophisticated energy function,
#' which is unfortunately hard to parameterize for different graphs. The
#' original publication did not disclose any parameter values, and the ones
#' below were determined by experimentation.
#'
#' The algorithm consists of two phases, an annealing phase, and a fine-tuning
#' phase. There is no simulated annealing in the second phase.
#'
#' Our implementation tries to follow the original publication, as much as
#' possible. The only major difference is that coordinates are explicitly kept
#' within the bounds of the rectangle of the layout.
#'
#' @param graph The graph to lay out. Edge directions are ignored.
#' @param coords Optional starting positions for the vertices. If this argument
#'   is not `NULL` then it should be an appropriate matrix of starting
#'   coordinates.
#' @param maxiter Number of iterations to perform in the first phase.
#' @param fineiter Number of iterations in the fine tuning phase.
#' @param cool.fact Cooling factor.
#' @param weight.node.dist Weight for the node-node distances component of the
#'   energy function.
#' @param weight.border Weight for the distance from the border component of
#'   the energy function. It can be set to zero, if vertices are allowed to sit
#'   on the border.
#' @param weight.edge.lengths Weight for the edge length component of the
#'   energy function.
#' @param weight.edge.crossings Weight for the edge crossing component of the
#'   energy function.
#' @param weight.node.edge.dist Weight for the node-edge distance component of
#'   the energy function.
#' @return A matrix with two columns, containing the x and y coordinates
#'   of the vertices:
#'   \describe{
#'     \item{x}{
#'       The x-coordinate of the vertex.
#'     }
#'     \item{y}{
#'       The y-coordinate of the vertex.
#'     }
#'   }
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout_with_fr()],
#' [layout_with_kk()] for other layout algorithms.
#' @references Ron Davidson, David Harel: Drawing Graphs Nicely Using Simulated
#' Annealing. *ACM Transactions on Graphics* 15(4), pp. 301-331, 1996.
#' @export
#' @family graph layouts
#' @examples
#'
#' set.seed(42)
#' ## Figures from the paper
#' g_1b <- make_star(19, mode = "undirected") + path(c(2:19, 2)) +
#'   path(c(seq(2, 18, by = 2), 2))
#' plot(g_1b, layout = layout_with_dh)
#'
#' g_2 <- make_lattice(c(8, 3)) + edges(1, 8, 9, 16, 17, 24)
#' plot(g_2, layout = layout_with_dh)
#'
#' g_3 <- make_empty_graph(n = 70)
#' plot(g_3, layout = layout_with_dh)
#'
#' g_4 <- make_empty_graph(n = 70, directed = FALSE) + edges(1:70)
#' plot(g_4, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_5a <- make_ring(24)
#' plot(g_5a, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_5b <- make_ring(40)
#' plot(g_5b, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_6 <- make_lattice(c(2, 2, 2))
#' plot(g_6, layout = layout_with_dh)
#'
#' g_7 <- graph_from_literal(1:3:5 - -2:4:6)
#' plot(g_7, layout = layout_with_dh, vertex.label = V(g_7)$name)
#'
#' g_8 <- make_ring(5) + make_ring(10) + make_ring(5) +
#'   edges(
#'     1, 6, 2, 8, 3, 10, 4, 12, 5, 14,
#'     7, 16, 9, 17, 11, 18, 13, 19, 15, 20
#'   )
#' plot(g_8, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_9 <- make_lattice(c(3, 2, 2))
#' plot(g_9, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_10 <- make_lattice(c(6, 6))
#' plot(g_10, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_11a <- make_tree(31, 2, mode = "undirected")
#' plot(g_11a, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_11b <- make_tree(21, 4, mode = "undirected")
#' plot(g_11b, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
#'
#' g_12 <- make_empty_graph(n = 37, directed = FALSE) +
#'   path(1:5, 10, 22, 31, 37:33, 27, 16, 6, 1) + path(6, 7, 11, 9, 10) + path(16:22) +
#'   path(27:31) + path(2, 7, 18, 28, 34) + path(3, 8, 11, 19, 29, 32, 35) +
#'   path(4, 9, 20, 30, 36) + path(1, 7, 12, 14, 19, 24, 26, 30, 37) +
#'   path(5, 9, 13, 15, 19, 23, 25, 28, 33) + path(3, 12, 16, 25, 35, 26, 22, 13, 3)
#' plot(g_12, layout = layout_with_dh, vertex.size = 5, vertex.label = NA)
layout_with_dh <- function(
  graph,
  coords = NULL,
  maxiter = 10,
  fineiter = max(10, log2(vcount(graph))),
  cool.fact = 0.75,
  weight.node.dist = 1.0,
  weight.border = 0.0,
  weight.edge.lengths = edge_density(graph) / 10,
  weight.edge.crossings = 1.0 - sqrt(edge_density(graph)),
  weight.node.edge.dist = 0.2 * (1 - edge_density(graph))
) {
  if (is.null(coords)) {
    coords <- matrix(NA_real_, ncol = 2, nrow = 0)
    use.seed <- FALSE
  } else {
    use.seed <- TRUE
  }

  layout_davidson_harel_impl(
    graph = graph,
    res = coords,
    use_seed = use.seed,
    maxiter = maxiter,
    fineiter = fineiter,
    cool_fact = cool.fact,
    weight_node_dist = weight.node.dist,
    weight_border = weight.border,
    weight_edge_lengths = weight.edge.lengths,
    weight_edge_crossings = weight.edge.crossings,
    weight_node_edge_dist = weight.node.edge.dist
  )
}


#' @rdname layout_with_dh
#' @param ... Passed to `layout_with_dh()`.
#' @export
with_dh <- function(...) layout_spec(layout_with_dh, ...)


## ----------------------------------------------------------------

#' The Fruchterman-Reingold layout algorithm
#'
#' Place vertices on the plane using the force-directed layout algorithm by
#' Fruchterman and Reingold.
#'
#' See the referenced paper below for the details of the algorithm.
#'
#' This function was rewritten from scratch in igraph version 0.8.0.
#'
#' @param graph The graph to lay out. Edge directions are ignored.
#' @param coords Optional starting positions for the vertices. If this argument
#'   is not `NULL` then it should be an appropriate matrix of starting
#'   coordinates.
#' @param dim Integer scalar, 2 or 3, the dimension of the layout.  Two
#'   dimensional layouts are places on a plane, three dimensional ones in the 3d
#'   space.
#' @param niter Integer scalar, the number of iterations to perform.
#' @param start.temp Real scalar, the start temperature. This is the maximum
#'   amount of movement alloved along one axis, within one step, for a vertex.
#'   Currently it is decreased linearly to zero during the iteration.
#' @param grid Character scalar, whether to use the faster, but less accurate
#'   grid based implementation of the algorithm. By default (\dQuote{auto}), the
#'   grid-based implementation is used if the graph has more than one thousand
#'   vertices.
#' @param weights A vector giving edge weights. The `weight` edge
#'   attribute is used by default, if present. If weights are given, then the
#'   attraction along the edges will be multiplied by the given edge weights.
#'   This places vertices connected with a highly weighted edge closer to
#'   each other. Weights must be positive.
#' @param minx If not `NULL`, then it must be a numeric vector that gives
#'   lower boundaries for the \sQuote{x} coordinates of the vertices. The length
#'   of the vector must match the number of vertices in the graph.
#' @param maxx Similar to `minx`, but gives the upper boundaries.
#' @param miny Similar to `minx`, but gives the lower boundaries of the
#'   \sQuote{y} coordinates.
#' @param maxy Similar to `minx`, but gives the upper boundaries of the
#'   \sQuote{y} coordinates.
#' @param minz Similar to `minx`, but gives the lower boundaries of the
#'   \sQuote{z} coordinates.
#' @param maxz Similar to `minx`, but gives the upper boundaries of the
#'   \sQuote{z} coordinates.
#' @param coolexp,maxdelta,area,repulserad `r lifecycle::badge("deprecated")` These
#'  arguments are not supported from igraph version 0.8.0 and are ignored
#'  (with a warning).
#' @param maxiter A deprecated synonym of `niter`, for compatibility.
#' @return A two- or three-column matrix, each row giving the coordinates of a
#'   vertex, according to the ids of the vertex ids.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout_with_drl()], [layout_with_kk()] for
#' other layout algorithms.
#' @references Fruchterman, T.M.J. and Reingold, E.M. (1991). Graph Drawing by
#' Force-directed Placement. *Software - Practice and Experience*,
#' 21(11):1129-1164.
#' @export
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' # Fixing ego
#' g <- sample_pa(20, m = 2)
#' minC <- rep(-Inf, vcount(g))
#' maxC <- rep(Inf, vcount(g))
#' minC[1] <- maxC[1] <- 0
#' co <- layout_with_fr(g,
#'   minx = minC, maxx = maxC,
#'   miny = minC, maxy = maxC
#' )
#' co[1, ]
#' plot(g,
#'   layout = co, vertex.size = 30, edge.arrow.size = 0.2,
#'   vertex.label = c("ego", rep("", vcount(g) - 1)), rescale = FALSE,
#'   xlim = range(co[, 1]), ylim = range(co[, 2]), vertex.label.dist = 0,
#'   vertex.label.color = "red"
#' )
#' axis(1)
#' axis(2)
#'
layout_with_fr <- function(
  graph,
  coords = NULL,
  dim = c(2, 3),
  niter = 500,
  start.temp = sqrt(vcount(graph)),
  grid = c("auto", "grid", "nogrid"),
  weights = NULL,
  minx = NULL,
  maxx = NULL,
  miny = NULL,
  maxy = NULL,
  minz = NULL,
  maxz = NULL,
  coolexp = deprecated(),
  maxdelta = deprecated(),
  area = deprecated(),
  repulserad = deprecated(),
  maxiter = deprecated()
) {
  # Argument checks
  ensure_igraph(graph)
  coords[] <- as.numeric(coords)
  dim <- igraph_match_arg(dim)
  if (!missing(niter) && !missing(maxiter)) {
    cli::cli_abort(c(
      "{.arg niter} and {.arg maxiter} must not be specified at the same time.",
      i = "Specify only {.arg niter}, {.arg maxiter} is deprecated."
    ))
  }
  if (!missing(maxiter)) {
    niter <- maxiter
  }
  niter <- as.numeric(niter)
  start.temp <- as.numeric(start.temp)

  grid <- igraph_match_arg(grid)
  grid <- switch(grid, "grid" = 0L, "nogrid" = 1L, "auto" = 2L)

  if (is.null(weights) && "weight" %in% edge_attr_names(graph)) {
    weights <- E(graph)$weight
  }
  if (!is.null(weights) && any(!is.na(weights))) {
    weights <- as.numeric(weights)
  } else {
    weights <- NULL
  }
  if (!is.null(minx)) {
    minx <- as.numeric(minx)
  }
  if (!is.null(maxx)) {
    maxx <- as.numeric(maxx)
  }
  if (!is.null(miny)) {
    miny <- as.numeric(miny)
  }
  if (!is.null(maxy)) {
    maxy <- as.numeric(maxy)
  }
  if (!is.null(minz)) {
    minz <- as.numeric(minz)
  }
  if (!is.null(maxz)) {
    maxz <- as.numeric(maxz)
  }
  if (lifecycle::is_present(coolexp)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_fr(coolexp = )")
  }
  if (lifecycle::is_present(maxdelta)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_fr(maxdelta = )")
  }
  if (lifecycle::is_present(area)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_fr(area = )")
  }
  if (lifecycle::is_present(repulserad)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_fr(repulserad = )")
  }

  on.exit(.Call(Rx_igraph_finalizer))
  if (dim == 2) {
    res <- .Call(
      Rx_igraph_layout_fruchterman_reingold,
      graph,
      coords,
      niter,
      start.temp,
      weights,
      minx,
      maxx,
      miny,
      maxy,
      grid
    )
  } else {
    res <- .Call(
      Rx_igraph_layout_fruchterman_reingold_3d,
      graph,
      coords,
      niter,
      start.temp,
      weights,
      minx,
      maxx,
      miny,
      maxy,
      minz,
      maxz
    )
  }
  res
}


#' @rdname layout_with_fr
#' @param ... Passed to `layout_with_fr()`.
#' @export
with_fr <- function(...) layout_spec(layout_with_fr, ...)

#' The Fruchterman-Reingold layout algorithm
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.fruchterman.reingold()` was renamed to [layout_with_fr()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.fruchterman.reingold <- function(..., params = list()) {
  lifecycle::deprecate_soft(
    "2.1.0",
    "layout.fruchterman.reingold()",
    "layout_with_fr()"
  )
  do_call(layout_with_fr, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' The GEM layout algorithm
#'
#' Place vertices on the plane using the GEM force-directed layout algorithm.
#'
#' See the referenced paper below for the details of the algorithm.
#'
#' @param graph The input graph. Edge directions are ignored.
#' @param coords If not `NULL`, then the starting coordinates should be
#'   given here, in a two or three column matrix, depending on the `dim`
#'   argument.
#' @param maxiter The maximum number of iterations to perform. Updating a
#'   single vertex counts as an iteration.  A reasonable default is 40 * n * n,
#'   where n is the number of vertices. The original paper suggests 4 * n * n,
#'   but this usually only works if the other parameters are set up carefully.
#' @param temp.max The maximum allowed local temperature. A reasonable default
#'   is the number of vertices.
#' @param temp.min The global temperature at which the algorithm terminates
#'   (even before reaching `maxiter` iterations). A reasonable default is
#'   1/10.
#' @param temp.init Initial local temperature of all vertices. A reasonable
#'   default is the square root of the number of vertices.
#' @return A numeric matrix with two columns, and as many rows as the number of
#'   vertices.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout_with_fr()],
#' [plot.igraph()], [tkplot()]
#' @references Arne Frick, Andreas Ludwig, Heiko Mehldau: A Fast Adaptive
#' Layout Algorithm for Undirected Graphs, *Proc. Graph Drawing 1994*,
#' LNCS 894, pp. 388-403, 1995.
#' @export
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' set.seed(42)
#' g <- make_ring(10)
#' plot(g, layout = layout_with_gem)
#'
layout_with_gem <- function(
  graph,
  coords = NULL,
  maxiter = 40 * vcount(graph)^2,
  temp.max = max(vcount(graph), 1),
  temp.min = 1 / 10,
  temp.init = sqrt(max(vcount(graph), 1))
) {
  if (is.null(coords)) {
    coords <- matrix(NA_real_, ncol = 2, nrow = 0)
    use.seed <- FALSE
  } else {
    use.seed <- TRUE
  }

  layout_gem_impl(
    graph = graph,
    res = coords,
    use_seed = use.seed,
    maxiter = maxiter,
    temp_max = temp.max,
    temp_min = temp.min,
    temp_init = temp.init
  )
}


#' @rdname layout_with_gem
#' @param ... Passed to `layout_with_gem()`.
#' @export
with_gem <- function(...) layout_spec(layout_with_gem, ...)


## ----------------------------------------------------------------

#' The graphopt layout algorithm
#'
#' A force-directed layout algorithm, that scales relatively well to large
#' graphs.
#'
#' `layout_with_graphopt()` is a port of the graphopt layout algorithm by Michael
#' Schmuhl. graphopt version 0.4.1 was rewritten in C and the support for
#' layers was removed (might be added later) and a code was a bit reorganized
#' to avoid some unnecessary steps is the node charge (see below) is zero.
#'
#' graphopt uses physical analogies for defining attracting and repelling
#' forces among the vertices and then the physical system is simulated until it
#' reaches an equilibrium. (There is no simulated annealing or anything like
#' that, so a stable fixed point is not guaranteed.)
#'
#' @param graph The input graph.
#' @param start If given, then it should be a matrix with two columns and one
#'   line for each vertex. This matrix will be used as starting positions for the
#'   algorithm. If not given, then a random starting matrix is used.
#' @param niter Integer scalar, the number of iterations to perform.  Should be
#'   a couple of hundred in general. If you have a large graph then you might
#'   want to only do a few iterations and then check the result. If it is not
#'   good enough you can feed it in again in the `start` argument. The
#'   default value is 500.
#' @param charge The charge of the vertices, used to calculate electric
#'   repulsion. The default is 0.001.
#' @param mass The mass of the vertices, used for the spring forces. The
#'   default is 30.
#' @param spring.length The length of the springs, an integer number. The
#'   default value is zero.
#' @param spring.constant The spring constant, the default value is one.
#' @param max.sa.movement Real constant, it gives the maximum amount of
#'   movement allowed in a single step along a single axis. The default value is
#'   5.
#' @return A numeric matrix with two columns, and a row for each vertex.
#' @author Michael Schmuhl for the original graphopt code, rewritten and
#' wrapped by Gabor Csardi \email{csardi.gabor@@gmail.com}.
#' @keywords graphs
#' @export
#' @family graph layouts
layout_with_graphopt <- function(
  graph,
  start = NULL,
  niter = 500,
  charge = 0.001,
  mass = 30,
  spring.length = 0,
  spring.constant = 1,
  max.sa.movement = 5
) {
  ensure_igraph(graph)
  start[] <- as.numeric(start)
  niter <- as.double(niter)
  charge <- as.double(charge)
  mass <- as.double(mass)
  spring.length <- as.double(spring.length)
  spring.constant <- as.double(spring.constant)
  max.sa.movement <- as.double(max.sa.movement)

  on.exit(.Call(Rx_igraph_finalizer))
  .Call(
    Rx_igraph_layout_graphopt,
    graph,
    niter,
    charge,
    mass,
    spring.length,
    spring.constant,
    max.sa.movement,
    start
  )
}


#' @rdname layout_with_graphopt
#' @param ... Passed to `layout_with_graphopt()`.
#' @export
with_graphopt <- function(...) layout_spec(layout_with_graphopt, ...)


## ----------------------------------------------------------------

#' The Kamada-Kawai layout algorithm
#'
#' Place the vertices on the plane, or in 3D space, based on a physical
#' model of springs.
#'
#' See the referenced paper below for the details of the algorithm.
#'
#' This function was rewritten from scratch in igraph version 0.8.0 and it
#' follows truthfully the original publication by Kamada and Kawai now.
#'
#' @param graph The input graph. Edge directions are ignored.
#' @param coords If not `NULL`, then the starting coordinates should be
#'   given here, in a two or three column matrix, depending on the `dim`
#'   argument.
#' @param dim Integer scalar, 2 or 3, the dimension of the layout.  Two
#'   dimensional layouts are places on a plane, three dimensional ones in the 3d
#'   space.
#' @param maxiter The maximum number of iterations to perform. The algorithm
#'   might terminate earlier, see the `epsilon` argument.
#' @param epsilon Numeric scalar, the algorithm terminates, if the maximal
#'   delta is less than this. (See the reference below for what delta means.) If
#'   you set this to zero, then the function always performs `maxiter`
#'   iterations.
#' @param kkconst Numeric scalar, the Kamada-Kawai vertex attraction constant.
#'   Typical (and default) value is the number of vertices.
#' @param weights Edge weights, larger values will result in longer edges.
#'   Note that this is the opposite of [layout_with_fr()], which produces
#'   shorter edges for larger weights. Weights must be positive.
#' @param minx If not `NULL`, then it must be a numeric vector that gives
#'   lower boundaries for the \sQuote{x} coordinates of the vertices. The length
#'   of the vector must match the number of vertices in the graph.
#' @param maxx Similar to `minx`, but gives the upper boundaries.
#' @param miny Similar to `minx`, but gives the lower boundaries of the
#'   \sQuote{y} coordinates.
#' @param maxy Similar to `minx`, but gives the upper boundaries of the
#'   \sQuote{y} coordinates.
#' @param minz Similar to `minx`, but gives the lower boundaries of the
#'   \sQuote{z} coordinates.
#' @param maxz Similar to `minx`, but gives the upper boundaries of the
#'   \sQuote{z} coordinates.
#' @param niter,sigma,initemp,coolexp `r lifecycle::badge("deprecated")` These
#' arguments are not supported from igraph version 0.8.0 and are ignored (with a warning).
#' @param start Deprecated synonym for `coords`, for compatibility.
#' @return A numeric matrix with two (dim=2) or three (dim=3) columns, and as
#'   many rows as the number of vertices, the x, y and potentially z coordinates
#'   of the vertices.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [layout_with_drl()], [plot.igraph()],
#' [tkplot()]
#' @references Kamada, T. and Kawai, S.: An Algorithm for Drawing General
#' Undirected Graphs. *Information Processing Letters*, 31/1, 7--15, 1989.
#' @export
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' g <- make_ring(10)
#' E(g)$weight <- rep(1:2, length.out = ecount(g))
#' plot(g, layout = layout_with_kk, edge.label = E(g)$weight)
#'
layout_with_kk <- function(
  graph,
  coords = NULL,
  dim = c(2, 3),
  maxiter = 50 * vcount(graph),
  epsilon = 0.0,
  kkconst = max(vcount(graph), 1),
  weights = NULL,
  minx = NULL,
  maxx = NULL,
  miny = NULL,
  maxy = NULL,
  minz = NULL,
  maxz = NULL,
  niter = deprecated(),
  sigma = deprecated(),
  initemp = deprecated(),
  coolexp = deprecated(),
  start = deprecated()
) {
  # Argument checks
  if (!missing(coords) && !missing(start)) {
    cli::cli_abort(c(
      "{.arg coords} and {.arg start} must not be specified at the same time.",
      i = "Specify only {.arg coords}, {.arg start} is deprecated."
    ))
  }
  if (!missing(start)) {
    coords <- start
  }

  ensure_igraph(graph)
  coords[] <- as.numeric(coords)
  dim <- igraph_match_arg(dim)

  maxiter <- as.numeric(maxiter)
  epsilon <- as.numeric(epsilon)
  kkconst <- as.numeric(kkconst)
  if (is.null(weights) && "weight" %in% edge_attr_names(graph)) {
    weights <- E(graph)$weight
  }
  if (!is.null(weights) && any(!is.na(weights))) {
    weights <- as.numeric(weights)
  } else {
    weights <- NULL
  }
  if (!is.null(minx)) {
    minx <- as.numeric(minx)
  }
  if (!is.null(maxx)) {
    maxx <- as.numeric(maxx)
  }
  if (!is.null(miny)) {
    miny <- as.numeric(miny)
  }
  if (!is.null(maxy)) {
    maxy <- as.numeric(maxy)
  }
  if (!is.null(minz)) {
    minz <- as.numeric(minz)
  }
  if (!is.null(maxz)) {
    maxz <- as.numeric(maxz)
  }

  if (lifecycle::is_present(niter)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_kk(niter = )")
  }
  if (lifecycle::is_present(sigma)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_kk(sigma = )")
  }
  if (lifecycle::is_present(initemp)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_kk(initemp = )")
  }
  if (lifecycle::is_present(coolexp)) {
    lifecycle::deprecate_stop("0.8.0", "layout_with_kk(coolexp = )")
  }

  on.exit(.Call(Rx_igraph_finalizer))
  # Function call
  if (dim == 2) {
    res <- .Call(
      Rx_igraph_layout_kamada_kawai,
      graph,
      coords,
      maxiter,
      epsilon,
      kkconst,
      weights,
      minx,
      maxx,
      miny,
      maxy
    )
  } else {
    res <- .Call(
      Rx_igraph_layout_kamada_kawai_3d,
      graph,
      coords,
      maxiter,
      epsilon,
      kkconst,
      weights,
      minx,
      maxx,
      miny,
      maxy,
      minz,
      maxz
    )
  }

  res
}


#' @rdname layout_with_kk
#' @param ... Passed to `layout_with_kk()`.
#' @export
#'
with_kk <- function(...) layout_spec(layout_with_kk, ...)

#' The Kamada-Kawai layout algorithm
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.kamada.kawai()` was renamed to [layout_with_kk()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.kamada.kawai <- function(..., params = list()) {
  lifecycle::deprecate_soft(
    "2.1.0",
    "layout.kamada.kawai()",
    "layout_with_kk()"
  )
  do_call(layout_with_kk, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' Large Graph Layout
#'
#' A layout generator for larger graphs.
#'
#' `layout_with_lgl()` is for large connected graphs, it is similar to the layout
#' generator of the Large Graph Layout software
#' (<https://lgl.sourceforge.net/>).
#'
#' @param graph The input graph
#' @param maxiter The maximum number of iterations to perform (150).
#' @param maxdelta The maximum change for a vertex during an iteration (the
#'   number of vertices).
#' @param area The area of the surface on which the vertices are placed (square
#'   of the number of vertices).
#' @param coolexp The cooling exponent of the simulated annealing (1.5).
#' @param repulserad Cancellation radius for the repulsion (the `area`
#'   times the number of vertices).
#' @param cellsize The size of the cells for the grid. When calculating the
#'   repulsion forces between vertices only vertices in the same or neighboring
#'   grid cells are taken into account (the fourth root of the number of
#'   `area`.
#' @param root The id of the vertex to place at the middle of the layout. The
#'   default value is -1 which means that a random vertex is selected.
#' @return A numeric matrix with two columns and as many rows as vertices.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @keywords graphs
#' @export
#' @family graph layouts
layout_with_lgl <- function(
  graph,
  maxiter = 150,
  maxdelta = vcount(graph),
  area = vcount(graph)^2,
  coolexp = 1.5,
  repulserad = area * vcount(graph),
  cellsize = sqrt(sqrt(area)),
  root = NULL
) {
  ensure_igraph(graph)
  if (is.null(root)) {
    root <- -1
  } else {
    root <- as_igraph_vs(graph, root) - 1
  }

  on.exit(.Call(Rx_igraph_finalizer))
  .Call(
    Rx_igraph_layout_lgl,
    graph,
    as.double(maxiter),
    as.double(maxdelta),
    as.double(area),
    as.double(coolexp),
    as.double(repulserad),
    as.double(cellsize),
    root
  )
}


#' @rdname layout_with_lgl
#' @param ... Passed to `layout_with_lgl()`.
#' @export
with_lgl <- function(...) layout_spec(layout_with_lgl, ...)

#' Large Graph Layout
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.lgl()` was renamed to [layout_with_lgl()] to create a more
#' consistent API.
#' @param ... Passed to the new layout functions.
#' @param params Passed to the new layout functions as arguments.
#' @keywords internal
#' @export
layout.lgl <- function(..., params = list()) {
  lifecycle::deprecate_soft("2.1.0", "layout.lgl()", "layout_with_lgl()")
  do_call(layout_with_lgl, .args = c(list(...), params))
}

## ----------------------------------------------------------------

#' Graph layout by multidimensional scaling
#'
#' Multidimensional scaling of some distance matrix defined on the vertices of
#' a graph.
#'
#' `layout_with_mds()` uses classical multidimensional scaling (Torgerson scaling)
#' for generating the coordinates. Multidimensional scaling aims to place points
#' from a higher dimensional space in a (typically) 2 dimensional plane, so that
#' the distances between the points are kept as much as this is possible.
#'
#' By default igraph uses the shortest path matrix as the distances between the
#' nodes, but the user can override this via the `dist` argument.
#'
#' Warning: If the graph is symmetric to the exchange of two vertices (as is the
#' case with leaves of a tree connecting to the same parent), classical
#' multidimensional scaling may assign the same coordinates to these vertices.
#'
#' This function generates the layout separately for each graph component and
#' then merges them via [merge_coords()].
#'
#' @param graph The input graph.
#' @param dist The distance matrix for the multidimensional scaling.  If
#'   `NULL` (the default), then the unweighted shortest path matrix is used.
#' @param dim `layout_with_mds()` supports dimensions up to the number of nodes
#'   minus one, but only if the graph is connected; for unconnected graphs, the
#'   only possible value is 2. This is because `merge_coords()` only works in
#'   2D.
#' @param options This is currently ignored, as ARPACK is not used any more for
#'   solving the eigenproblem
#' @return A numeric matrix with `dim` columns.
#' @author Tamas Nepusz \email{ntamas@@gmail.com} and Gabor Csardi
#' \email{csardi.gabor@@gmail.com}
#' @seealso [layout()], [plot.igraph()]
#' @references Cox, T. F. and Cox, M. A. A. (2001) *Multidimensional
#' Scaling*.  Second edition. Chapman and Hall.
#' @export
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' g <- sample_gnp(100, 2 / 100)
#' l <- layout_with_mds(g)
#' plot(g, layout = l, vertex.label = NA, vertex.size = 3)
layout_with_mds <- function(
  graph,
  dist = NULL,
  dim = 2,
  options = arpack_defaults()
) {
  if (is.function(options)) {
    lifecycle::deprecate_soft(
      "1.6.0",
      "layout_with_mds(options = 'must be a list')",
      details = c(
        "`arpack_defaults()` is now a function, use `options = arpack_defaults()` instead of `options = arpack_defaults`."
      )
    )
    options <- options()
  }

  # Argument checks
  ensure_igraph(graph)
  dist[] <- as.numeric(dist)
  dim <- as.numeric(dim)

  # Function call
  layout_mds_impl(
    graph = graph,
    dist = dist,
    dim = dim
  )
}


#' @rdname layout_with_mds
#' @param ... Passed to `layout_with_mds()`.
#' @export
with_mds <- function(...) layout_spec(layout_with_mds, ...)


## ----------------------------------------------------------------

#' The Sugiyama graph layout generator
#'
#' Sugiyama layout algorithm for layered directed acyclic graphs. The algorithm
#' minimized edge crossings.
#'
#' This layout algorithm is designed for directed acyclic graphs where each
#' vertex is assigned to a layer. Layers are indexed from zero, and vertices of
#' the same layer will be placed on the same horizontal line. The X coordinates
#' of vertices within each layer are decided by the heuristic proposed by
#' Sugiyama et al. to minimize edge crossings.
#'
#' You can also try to lay out undirected graphs, graphs containing cycles, or
#' graphs without an a priori layered assignment with this algorithm. igraph
#' will try to eliminate cycles and assign vertices to layers, but there is no
#' guarantee on the quality of the layout in such cases.
#'
#' The Sugiyama layout may introduce \dQuote{bends} on the edges in order to
#' obtain a visually more pleasing layout. This is achieved by adding dummy
#' nodes to edges spanning more than one layer. The resulting layout assigns
#' coordinates not only to the nodes of the original graph but also to the
#' dummy nodes. The layout algorithm will also return the extended graph with
#' the dummy nodes.
#'
#' For more details, see the reference below.
#'
#' @param graph The input graph.
#' @param layers A numeric vector or `NULL`. If not `NULL`, then it
#'   should specify the layer index of the vertices. Layers are numbered from
#'   one. If `NULL`, then igraph calculates the layers automatically.
#' @param hgap Real scalar, the minimum horizontal gap between vertices in the
#'   same layer.
#' @param vgap Real scalar, the distance between layers.
#' @param maxiter Integer scalar, the maximum number of iterations in the
#'   crossing minimization stage. 100 is a reasonable default; if you feel that
#'   you have too many edge crossings, increase this.
#' @param weights Optional edge weight vector. If `NULL`, then the
#'   'weight' edge attribute is used, if there is one. Supply `NA` here and
#'   igraph ignores the edge weights. These are used only if the graph
#'   contains cycles; igraph will tend to reverse edges with smaller weights
#'   when breaking the cycles.
#' @param attributes Which graph/vertex/edge attributes to keep in the extended
#'   graph. \sQuote{default} keeps the \sQuote{size}, \sQuote{size2},
#'   \sQuote{shape}, \sQuote{label} and \sQuote{color} vertex attributes and the
#'   \sQuote{arrow.mode} and \sQuote{arrow.size} edge attributes. \sQuote{all}
#'   keep all graph, vertex and edge attributes, \sQuote{none} keeps none of
#'   them.
#' @return A list with the components:
#' \describe{
#'     \item{layout}{
#'       The layout, a two-column matrix, for the original graph vertices.
#'     }
#'     \item{layout.dummy}{
#'       The layout for the dummy vertices, a two column matrix.
#'     }
#'     \item{extd_graph}{
#'       The original graph, extended with dummy vertices.
#'       The \sQuote{dummy} vertex attribute is set on this graph,
#'       it is a logical attributes, and it tells you whether the vertex is a dummy vertex.
#'       The \sQuote{layout} graph attribute is also set,
#'       and it is the layout matrix for all (original and dummy) vertices.
#'     }
#'   }
#' @author Tamas Nepusz \email{ntamas@@gmail.com}
#' @references K. Sugiyama, S. Tagawa and M. Toda, "Methods for Visual
#' Understanding of Hierarchical Systems". IEEE Transactions on Systems, Man
#' and Cybernetics 11(2):109-125, 1981.
#' @export
#' @importFrom utils head
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' ## Data taken from http://tehnick-8.narod.ru/dc_clients/
#' DC <- graph_from_literal(
#'   "DC++" -+ "LinuxDC++":"BCDC++":"EiskaltDC++":"StrongDC++":"DiCe!++",
#'   "LinuxDC++" -+ "FreeDC++", "BCDC++" -+ "StrongDC++",
#'   "FreeDC++" -+ "BMDC++":"EiskaltDC++",
#'   "StrongDC++" -+ "AirDC++":"zK++":"ApexDC++":"TkDC++",
#'   "StrongDC++" -+ "StrongDC++ SQLite":"RSX++",
#'   "ApexDC++" -+ "FlylinkDC++ ver <= 4xx",
#'   "ApexDC++" -+ "ApexDC++ Speed-Mod":"DiCe!++",
#'   "StrongDC++ SQLite" -+ "FlylinkDC++ ver >= 5xx",
#'   "ApexDC++ Speed-Mod" -+ "FlylinkDC++ ver <= 4xx",
#'   "ApexDC++ Speed-Mod" -+ "GreylinkDC++",
#'   "FlylinkDC++ ver <= 4xx" -+ "FlylinkDC++ ver >= 5xx",
#'   "FlylinkDC++ ver <= 4xx" -+ AvaLink,
#'   "GreylinkDC++" -+ AvaLink:"RayLinkDC++":"SparkDC++":PeLink
#' )
#'
#' ## Use edge types
#' E(DC)$lty <- 1
#' E(DC)["BCDC++" %->% "StrongDC++"]$lty <- 2
#' E(DC)["FreeDC++" %->% "EiskaltDC++"]$lty <- 2
#' E(DC)["ApexDC++" %->% "FlylinkDC++ ver <= 4xx"]$lty <- 2
#' E(DC)["ApexDC++" %->% "DiCe!++"]$lty <- 2
#' E(DC)["StrongDC++ SQLite" %->% "FlylinkDC++ ver >= 5xx"]$lty <- 2
#' E(DC)["GreylinkDC++" %->% "AvaLink"]$lty <- 2
#'
#' ## Layers, as on the plot
#' layers <- list(
#'   c("DC++"),
#'   c("LinuxDC++", "BCDC++"),
#'   c("FreeDC++", "StrongDC++"),
#'   c(
#'     "BMDC++", "EiskaltDC++", "AirDC++", "zK++", "ApexDC++",
#'     "TkDC++", "RSX++"
#'   ),
#'   c("StrongDC++ SQLite", "ApexDC++ Speed-Mod", "DiCe!++"),
#'   c("FlylinkDC++ ver <= 4xx", "GreylinkDC++"),
#'   c(
#'     "FlylinkDC++ ver >= 5xx", "AvaLink", "RayLinkDC++",
#'     "SparkDC++", "PeLink"
#'   )
#' )
#'
#' ## Check that we have all nodes
#' all(sort(unlist(layers)) == sort(V(DC)$name))
#'
#' ## Add some graphical parameters
#' V(DC)$color <- "white"
#' V(DC)$shape <- "rectangle"
#' V(DC)$size <- 20
#' V(DC)$size2 <- 10
#' V(DC)$label <- lapply(V(DC)$name, function(x) {
#'   paste(strwrap(x, 12), collapse = "\n")
#' })
#' E(DC)$arrow.size <- 0.5
#'
#' ## Create a similar layout using the predefined layers
#' lay1 <- layout_with_sugiyama(DC, layers = apply(sapply(
#'   layers,
#'   function(x) V(DC)$name %in% x
#' ), 1, which))
#'
#' ## Simple plot, not very nice
#' par(mar = rep(.1, 4))
#' plot(DC, layout = lay1$layout, vertex.label.cex = 0.5)
#'
#' ## Sugiyama plot
#' plot(lay1$extd_graph, vertex.label.cex = 0.5)
#'
#' ## The same with automatic layer calculation
#' ## Keep vertex/edge attributes in the extended graph
#' lay2 <- layout_with_sugiyama(DC, attributes = "all")
#' plot(lay2$extd_graph, vertex.label.cex = 0.5)
#'
#' ## Another example, from the following paper:
#' ## Markus Eiglsperger, Martin Siebenhaller, Michael Kaufmann:
#' ## An Efficient Implementation of Sugiyama's Algorithm for
#' ## Layered Graph Drawing, Journal of Graph Algorithms and
#' ## Applications 9, 305--325 (2005).
#'
#' ex <- graph_from_literal(
#'   0 -+ 29:6:5:20:4,
#'   1 -+ 12,
#'   2 -+ 23:8,
#'   3 -+ 4,
#'   4,
#'   5 -+ 2:10:14:26:4:3,
#'   6 -+ 9:29:25:21:13,
#'   7,
#'   8 -+ 20:16,
#'   9 -+ 28:4,
#'   10 -+ 27,
#'   11 -+ 9:16,
#'   12 -+ 9:19,
#'   13 -+ 20,
#'   14 -+ 10,
#'   15 -+ 16:27,
#'   16 -+ 27,
#'   17 -+ 3,
#'   18 -+ 13,
#'   19 -+ 9,
#'   20 -+ 4,
#'   21 -+ 22,
#'   22 -+ 8:9,
#'   23 -+ 9:24,
#'   24 -+ 12:15:28,
#'   25 -+ 11,
#'   26 -+ 18,
#'   27 -+ 13:19,
#'   28 -+ 7,
#'   29 -+ 25
#' )
#'
#' layers <- list(
#'   0, c(5, 17), c(2, 14, 26, 3), c(23, 10, 18), c(1, 24),
#'   12, 6, c(29, 21), c(25, 22), c(11, 8, 15), 16, 27, c(13, 19),
#'   c(9, 20), c(4, 28), 7
#' )
#'
#' layex <- layout_with_sugiyama(ex, layers = apply(
#'   sapply(
#'     layers,
#'     function(x) V(ex)$name %in% as.character(x)
#'   ),
#'   1, which
#' ))
#'
#' origvert <- c(rep(TRUE, vcount(ex)), rep(FALSE, nrow(layex$layout.dummy)))
#' realedge <- as_edgelist(layex$extd_graph)[, 2] <= vcount(ex)
#' plot(layex$extd_graph,
#'   vertex.label.cex = 0.5,
#'   edge.arrow.size = .5,
#'   vertex.size = ifelse(origvert, 5, 0),
#'   vertex.shape = ifelse(origvert, "square", "none"),
#'   vertex.label = ifelse(origvert, V(ex)$name, ""),
#'   edge.arrow.mode = ifelse(realedge, 2, 0)
#' )
#'
layout_with_sugiyama <- function(
  graph,
  layers = NULL,
  hgap = 1,
  vgap = 1,
  maxiter = 100,
  weights = NULL,
  attributes = c("default", "all", "none")
) {
  attributes <- igraph_match_arg(attributes)

  res <- layout_sugiyama_impl(
    graph = graph,
    layers = layers,
    hgap = hgap,
    vgap = vgap,
    maxiter = maxiter,
    weights = weights
  )

  # Flip the y coordinates, more natural this way
  vc <- vcount(graph)
  if (vc > 0) {
    res$res[, 2] <- max(res$res[, 2]) - res$res[, 2] + 1
  }

  # Separate real and dummy vertices
  if (nrow(res$res) == vc) {
    res$layout <- res$res
    res$layout.dummy <- matrix(NA_real_, nrow = 0, ncol = 2)
  } else {
    res$layout <- res$res[seq_len(vc), ]
    res$layout.dummy <- res$res[(vc + 1):nrow(res$res), , drop = FALSE]
  }

  # Add some attributes to the extended graph
  E(res$extd_graph)$orig <- res$extd_to_orig_eids
  res$extd_to_orig_eids <- NULL

  res$extd_graph <- set_vertex_attr(
    res$extd_graph,
    "dummy",
    value = c(
      rep(FALSE, vc),
      rep(TRUE, nrow(res$res) - vc)
    )
  )

  res$extd_graph$layout <- rbind(res$layout, res$layout.dummy)

  if (attributes == "default" || attributes == "all") {
    if ("size" %in% vertex_attr_names(graph)) {
      V(res$extd_graph)$size <- 0
      V(res$extd_graph)$size[!V(res$extd_graph)$dummy] <- V(graph)$size
    }
    if ("size2" %in% vertex_attr_names(graph)) {
      V(res$extd_graph)$size2 <- 0
      V(res$extd_graph)$size2[!V(res$extd_graph)$dummy] <- V(graph)$size2
    }
    if ("shape" %in% vertex_attr_names(graph)) {
      V(res$extd_graph)$shape <- "none"
      V(res$extd_graph)$shape[!V(res$extd_graph)$dummy] <- V(graph)$shape
    }
    if ("label" %in% vertex_attr_names(graph)) {
      V(res$extd_graph)$label <- ""
      V(res$extd_graph)$label[!V(res$extd_graph)$dummy] <- V(graph)$label
    }
    if ("color" %in% vertex_attr_names(graph)) {
      V(res$extd_graph)$color <- head(V(graph)$color, 1)
      V(res$extd_graph)$color[!V(res$extd_graph)$dummy] <- V(graph)$color
    }
    eetar <- as_edgelist(res$extd_graph, names = FALSE)[, 2]
    E(res$extd_graph)$arrow.mode <- 0
    if ("arrow.mode" %in% edge_attr_names(graph)) {
      E(res$extd_graph)$arrow.mode[eetar <= vc] <- E(graph)$arrow.mode
    } else {
      E(res$extd_graph)$arrow.mode[eetar <= vc] <- is_directed(graph) * 2
    }
    if ("arrow.size" %in% edge_attr_names(graph)) {
      E(res$extd_graph)$arrow.size <- 0
      E(res$extd_graph)$arrow.size[eetar <= vc] <- E(graph)$arrow.size
    }
  }

  if (attributes == "all") {
    gatt <- setdiff(graph_attr_names(graph), "layout")
    vatt <- setdiff(
      vertex_attr_names(graph),
      c("size", "size2", "shape", "label", "color")
    )
    eatt <- setdiff(
      edge_attr_names(graph),
      c("arrow.mode", "arrow.size")
    )
    for (ga in gatt) {
      res$extd_graph <- set_graph_attr(
        res$extd_graph,
        ga,
        graph_attr(graph, ga)
      )
    }
    for (va in vatt) {
      notdummy <- which(!V(res$extd_graph)$dummy)
      res$extd_graph <- set_vertex_attr(
        res$extd_graph,
        va,
        notdummy,
        vertex_attr(graph, va)
      )
    }
    for (ea in eatt) {
      eanew <- edge_attr(graph, ea)[E(res$extd_graph)$orig]
      res$extd_graph <- set_edge_attr(res$extd_graph, ea, value = eanew)
    }
  }

  res$res <- NULL
  res
}


#' @rdname layout_with_sugiyama
#' @param ... Passed to `layout_with_sugiyama()`.
#' @export
with_sugiyama <- function(...) layout_spec(layout_with_sugiyama, ...)


## ----------------------------------------------------------------

#' Merging graph layouts
#'
#' Place several graphs on the same layout
#'
#' `merge_coords()` takes a list of graphs and a list of coordinates and
#' places the graphs in a common layout. The method to use is chosen via the
#' `method` parameter, although right now only the `dla` method is
#' implemented.
#'
#' The `dla` method covers the graph with circles.  Then it sorts the
#' graphs based on the number of vertices first and places the largest graph at
#' the center of the layout. Then the other graphs are placed in decreasing
#' order via a DLA (diffision limited aggregation) algorithm: the graph is
#' placed randomly on a circle far away from the center and a random walk is
#' conducted until the graph walks into the larger graphs already placed or
#' walks too far from the center of the layout.
#'
#' The `layout_components()` function disassembles the graph first into
#' maximal connected components and calls the supplied `layout` function
#' for each component separately. Finally it merges the layouts via calling
#' `merge_coords()`.
#'
#' @param graphs A list of graph objects.
#' @param layouts A list of two-column matrices.
#' @param method Character constant giving the method to use. Right now only
#'   `dla` is implemented.
#' @param layout A function object, the layout function to use.
#' @param \dots Additional arguments to pass to the `layout` layout
#'   function.
#' @return A matrix with two columns and as many lines as the total number of
#'   vertices in the graphs.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @seealso [plot.igraph()], [tkplot()],
#' [layout()], [disjoint_union()]
#' @export
#' @family graph layouts
#' @keywords graphs
#' @examples
#'
#' # create 20 scale-free graphs and place them in a common layout
#' graphs <- lapply(sample(5:20, 20, replace = TRUE),
#'   barabasi.game,
#'   directed = FALSE
#' )
#' layouts <- lapply(graphs, layout_with_kk)
#' lay <- merge_coords(graphs, layouts)
#' g <- disjoint_union(graphs)
#' plot(g, layout = lay, vertex.size = 3, labels = NA, edge.color = "black")
merge_coords <- function(graphs, layouts, method = "dla") {
  lapply(graphs, ensure_igraph)
  if (method != "dla") {
    cli::cli_abort("{.arg method} must be {.str dla}, not {.str {method}}.")
  }

  on.exit(.Call(Rx_igraph_finalizer))
  .Call(
    Rx_igraph_layout_merge_dla,
    graphs,
    layouts
  )
}


#' Normalize coordinates for plotting graphs
#'
#' Rescale coordinates linearly to be within given bounds.
#'
#' `norm_coords()` normalizes a layout, it linearly transforms each
#' coordinate separately to fit into the given limits.
#'
#' @param layout A matrix with two or three columns, the layout to normalize.
#' @param xmin,xmax The limits for the first coordinate, if one of them or both
#'   are `NULL` then no normalization is performed along this direction.
#' @param ymin,ymax The limits for the second coordinate, if one of them or
#'   both are `NULL` then no normalization is performed along this
#'   direction.
#' @param zmin,zmax The limits for the third coordinate, if one of them or both
#'   are `NULL` then no normalization is performed along this direction.
#' @return A numeric matrix with at the same dimension as `layout`.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
#' @export
#' @family graph layouts
#' @keywords graphs
norm_coords <- function(
  layout,
  xmin = -1,
  xmax = 1,
  ymin = -1,
  ymax = 1,
  zmin = -1,
  zmax = 1
) {
  if (!is.matrix(layout)) {
    cli::cli_abort(
      "{.arg layout} must be a matrix, not {.obj_type_friendly {layout}}."
    )
  }
  if (!(ncol(layout) %in% c(2, 3))) {
    cli::cli_abort(
      "{.arg layout} should have 2 or 3 columns, not {ncol(layout)}."
    )
  }

  if (!is.null(xmin) && !is.null(xmax)) {
    layout[, 1] <- .layout.norm.col(layout[, 1], xmin, xmax)
  }

  if (!is.null(ymin) && !is.null(ymax)) {
    layout[, 2] <- .layout.norm.col(layout[, 2], ymin, ymax)
  }

  if (ncol(layout) == 3 && !is.null(zmin) && !is.null(zmax)) {
    layout[, 3] <- .layout.norm.col(layout[, 3], zmin, zmax)
  }

  layout
}

.layout.norm.col <- function(v, min, max) {
  # Likely cause: division by zero upstream
  if (all(is.nan(v))) {
    diff <- 0
  } else {
    vr <- range(v)
    diff <- diff(vr)
  }

  if (diff == 0) {
    return(rep((min + max) / 2, length(v)))
  }

  fac <- (max - min) / diff
  (v - vr[[1]]) * fac + min
}

#' @rdname merge_coords
#' @param graph The input graph.
#' @export
layout_components <- function(graph, layout = layout_with_kk, ...) {
  ensure_igraph(graph)

  V(graph)$id <- seq(vcount(graph))
  gl <- decompose(graph)
  ll <- lapply(gl, layout, ...)

  l <- merge_coords(gl, ll)
  l[unlist(sapply(gl, vertex_attr, "id")), ] <- l[]
  l
}

#' Spring layout, this was removed from igraph

#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' Now it calls the Fruchterman-Reingold layout [layout_with_fr()].
#'
#' @param graph Input graph.
#' @param ... Extra arguments are ignored.
#' @return Layout coordinates, a two column matrix.
#'
#' @export
#' @keywords internal
layout.spring <- function(graph, ...) {
  lifecycle::deprecate_warn("2.1.0", "layout.spring()", "layout_with_fr()")
  layout_with_fr(graph)
}

#' SVD layout, this was removed from igraph
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' Now it calls the Fruchterman-Reingold layout [layout_with_fr()].
#'
#' @param graph Input graph.
#' @param ... Extra arguments are ignored.
#' @return Layout coordinates, a two column matrix.
#'
#' @keywords internal
#' @export
layout.svd <- function(graph, ...) {
  lifecycle::deprecate_warn("2.1.0", "layout.svd()", "layout_with_fr()")
  layout_with_fr(graph)
}

#' Grid Fruchterman-Reingold layout, this was removed from igraph
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#'
#' Now it calls the Fruchterman-Reingold layout [layout_with_fr()].
#'
#' @param graph Input graph.
#' @param ... Extra arguments are ignored.
#' @return Layout coordinates, a two column matrix.
#'
#' @keywords internal
#' @export
layout.fruchterman.reingold.grid <- function(graph, ...) {
  lifecycle::deprecate_warn(
    "2.1.0",
    "layout.fruchterman.reingold.grid()",
    "layout_with_fr()"
  )
  layout_with_fr(graph)
}

#' The DrL graph layout generator
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `layout.drl()` was renamed to [layout_with_drl()] to create a more
#' consistent API.
#' @inheritParams layout_with_drl
#' @keywords internal
#' @export
layout.drl <- function(
  graph,
  use.seed = FALSE,
  seed = matrix(runif(vcount(graph) * 2), ncol = 2),
  options = drl_defaults$default,
  weights = NULL,
  dim = 2
) {
  # nocov start
  lifecycle::deprecate_soft("2.0.0", "layout.drl()", "layout_with_drl()")
  layout_with_drl(
    graph = graph,
    use.seed = use.seed,
    seed = seed,
    options = options,
    weights = weights,
    dim = dim
  )
} # nocov end

#' The DrL graph layout generator
#'
#' DrL is a force-directed graph layout toolbox focused on real-world
#' large-scale graphs, developed by Shawn Martin and colleagues at Sandia
#' National Laboratories.
#'
#' This function implements the force-directed DrL layout generator.
#'
#' The generator has the following parameters:
#' \describe{
#'     \item{edge.cut}{
#'       Edge cutting is done in the late stages of the algorithm in order to achieve less dense layouts.  Edges are cut if there is a lot of stress on them (a large value in the objective function sum). The edge cutting parameter is a value between 0 and 1 with 0 representing no edge cutting and 1 representing maximal edge cutting.
#'     }
#'     \item{init.iterations}{
#'       Number of iterations in the first phase.
#'     }
#'     \item{init.temperature}{
#'       Start temperature, first phase.
#'     }
#'     \item{init.attraction}{
#'       Attraction, first phase.
#'     }
#'     \item{init.damping.mult}{
#'       Damping, first phase.
#'     }
#'     \item{liquid.iterations}{
#'       Number of iterations, liquid phase.
#'     }
#'     \item{liquid.temperature}{
#'       Start temperature, liquid phase.
#'     }
#'     \item{liquid.attraction}{
#'       Attraction, liquid phase.
#'     }
#'     \item{liquid.damping.mult}{
#'       Damping, liquid phase.
#'     }
#'     \item{expansion.iterations}{
#'       Number of iterations, expansion phase.
#'     }
#'     \item{expansion.temperature}{
#'       Start temperature, expansion phase.
#'     }
#'     \item{expansion.attraction}{
#'       Attraction, expansion phase.
#'     }
#'     \item{expansion.damping.mult}{
#'       Damping, expansion phase.
#'     }
#'     \item{cooldown.iterations}{
#'       Number of iterations, cooldown phase.
#'     }
#'     \item{cooldown.temperature}{
#'       Start temperature, cooldown phase.
#'     }
#'     \item{cooldown.attraction}{
#'       Attraction, cooldown phase.
#'     }
#'     \item{cooldown.damping.mult}{
#'       Damping, cooldown phase.
#'     }
#'     \item{crunch.iterations}{
#'       Number of iterations, crunch phase.
#'     }
#'     \item{crunch.temperature}{
#'       Start temperature, crunch phase.
#'     }
#'     \item{crunch.attraction}{
#'       Attraction, crunch phase.
#'     }
#'     \item{crunch.damping.mult}{
#'       Damping, crunch phase.
#'     }
#'     \item{simmer.iterations}{
#'       Number of iterations, simmer phase.
#'     }
#'     \item{simmer.temperature}{
#'       Start temperature, simmer phase.
#'     }
#'     \item{simmer.attraction}{
#'       Attraction, simmer phase.
#'     }
#'     \item{simmer.damping.mult}{
#'       Damping, simmer phase.
#'     }
#'   }
#'
#' There are five pre-defined parameter settings as well, these are called
#' `drl_defaults$default`, `drl_defaults$coarsen`,
#' `drl_defaults$coarsest`, `drl_defaults$refine` and
#' `drl_defaults$final`.
#'
#' @aliases drl_defaults igraph.drl.coarsen
#' @aliases  igraph.drl.coarsest igraph.drl.default igraph.drl.final
#'  igraph.drl.refine
#' @param graph The input graph, in can be directed or undirected.
#' @param use.seed Logical scalar, whether to use the coordinates given in the
#'   `seed` argument as a starting point.
#' @param seed A matrix with two columns, the starting coordinates for the
#'   vertices is `use.seed` is `TRUE`. It is ignored otherwise.
#' @param options Options for the layout generator, a named list. See details
#'   below.
#' @param weights The weights of the edges. It must be a positive numeric vector,
#'   `NULL` or `NA`. If it is `NULL` and the input graph has a
#'   \sQuote{weight} edge attribute, then that attribute will be used. If
#'   `NULL` and no such attribute is present, then the edges will have equal
#'   weights. Set this to `NA` if the graph was a \sQuote{weight} edge
#'   attribute, but you don't want to use it for the layout. Larger edge weights
#'   correspond to stronger connections.
#' @param dim Either \sQuote{2} or \sQuote{3}, it specifies whether we want a
#'   two dimensional or a three dimensional layout. Note that because of the
#'   nature of the DrL algorithm, the three dimensional layout takes
#'   significantly longer to compute.
#' @return A numeric matrix with two columns.
#' @author Shawn Martin (<https://www.cs.otago.ac.nz/homepages/smartin/>)
#' and Gabor Csardi \email{csardi.gabor@@gmail.com} for the R/igraph interface
#' and the three dimensional version.
#' @seealso [layout()] for other layout generators.
#' @references See the following technical report: Martin, S., Brown, W.M.,
#' Klavans, R., Boyack, K.W., DrL: Distributed Recursive (Graph) Layout. SAND
#' Reports, 2008. 2936: p. 1-10.
#' @family layout_drl
#' @export
#' @importFrom stats runif
#' @keywords graphs
#' @examples
#'
#' g <- as_undirected(sample_pa(100, m = 1))
#' l <- layout_with_drl(g, options = list(simmer.attraction = 0))
#' plot(g, layout = l, vertex.size = 3, vertex.label = NA)
#'
layout_with_drl <- function(
  graph,
  use.seed = FALSE,
  seed = matrix(runif(vcount(graph) * 2), ncol = 2),
  options = drl_defaults$default,
  weights = NULL,
  dim = c(2, 3)
) {
  ensure_igraph(graph)

  dim <- igraph_match_arg(dim)

  use.seed <- as.logical(use.seed)
  seed <- as.matrix(seed)

  options <- modify_list(drl_defaults$default, options)

  if (is.null(weights) && "weight" %in% edge_attr_names(graph)) {
    weights <- E(graph)$weight
  }
  if (!is.null(weights) && !any(is.na(weights))) {
    weights <- as.numeric(weights)
  } else {
    weights <- NULL
  }

  on.exit(.Call(Rx_igraph_finalizer))
  if (dim == 2) {
    res <- .Call(
      Rx_igraph_layout_drl,
      graph,
      seed,
      use.seed,
      options,
      weights
    )
  } else {
    res <- .Call(
      Rx_igraph_layout_drl_3d,
      graph,
      seed,
      use.seed,
      options,
      weights
    )
  }
  res
}


#' @rdname layout_with_drl
#' @param ... Passed to `layout_with_drl()`.
#' @family layout_drl
#' @export
with_drl <- function(...) layout_spec(layout_with_drl, ...)


#' @family layout_drl
#' @export
igraph.drl.default <- list(
  edge.cut = 32 / 40,
  init.iterations = 0,
  init.temperature = 2000,
  init.attraction = 10,
  init.damping.mult = 1.0,
  liquid.iterations = 200,
  liquid.temperature = 2000,
  liquid.attraction = 10,
  liquid.damping.mult = 1.0,
  expansion.iterations = 200,
  expansion.temperature = 2000,
  expansion.attraction = 2,
  expansion.damping.mult = 1.0,
  cooldown.iterations = 200,
  cooldown.temperature = 2000,
  cooldown.attraction = 1,
  cooldown.damping.mult = .1,
  crunch.iterations = 50,
  crunch.temperature = 250,
  crunch.attraction = 1,
  crunch.damping.mult = 0.25,
  simmer.iterations = 100,
  simmer.temperature = 250,
  simmer.attraction = .5,
  simmer.damping.mult = 0
)

#' @family layout_drl
#' @export
igraph.drl.coarsen <- list(
  edge.cut = 32 / 40,
  init.iterations = 0,
  init.temperature = 2000,
  init.attraction = 10,
  init.damping.mult = 1.0,
  liquid.iterations = 200,
  liquid.temperature = 2000,
  liquid.attraction = 2,
  liquid.damping.mult = 1.0,
  expansion.iterations = 200,
  expansion.temperature = 2000,
  expansion.attraction = 10,
  expansion.damping.mult = 1.0,
  cooldown.iterations = 200,
  cooldown.temperature = 2000,
  cooldown.attraction = 1,
  cooldown.damping.mult = .1,
  crunch.iterations = 50,
  crunch.temperature = 250,
  crunch.attraction = 1,
  crunch.damping.mult = 0.25,
  simmer.iterations = 100,
  simmer.temperature = 250,
  simmer.attraction = .5,
  simmer.damping.mult = 0
)

#' @family layout_drl
#' @export
igraph.drl.coarsest <- list(
  edge.cut = 32 / 40,
  init.iterations = 0,
  init.temperature = 2000,
  init.attraction = 10,
  init.damping.mult = 1.0,
  liquid.iterations = 200,
  liquid.temperature = 2000,
  liquid.attraction = 2,
  liquid.damping.mult = 1.0,
  expansion.iterations = 200,
  expansion.temperature = 2000,
  expansion.attraction = 10,
  expansion.damping.mult = 1.0,
  cooldown.iterations = 200,
  cooldown.temperature = 2000,
  cooldown.attraction = 1,
  cooldown.damping.mult = .1,
  crunch.iterations = 200,
  crunch.temperature = 250,
  crunch.attraction = 1,
  crunch.damping.mult = 0.25,
  simmer.iterations = 100,
  simmer.temperature = 250,
  simmer.attraction = .5,
  simmer.damping.mult = 0
)

#' @family layout_drl
#' @export
igraph.drl.refine <- list(
  edge.cut = 32 / 40,
  init.iterations = 0,
  init.temperature = 50,
  init.attraction = .5,
  init.damping.mult = 1.0,
  liquid.iterations = 0,
  liquid.temperature = 2000,
  liquid.attraction = 2,
  liquid.damping.mult = 1.0,
  expansion.iterations = 50,
  expansion.temperature = 500,
  expansion.attraction = .1,
  expansion.damping.mult = .25,
  cooldown.iterations = 50,
  cooldown.temperature = 250,
  cooldown.attraction = 1,
  cooldown.damping.mult = .1,
  crunch.iterations = 50,
  crunch.temperature = 250,
  crunch.attraction = 1,
  crunch.damping.mult = 0.25,
  simmer.iterations = 0,
  simmer.temperature = 250,
  simmer.attraction = .5,
  simmer.damping.mult = 0
)

#' @family layout_drl
#' @export
igraph.drl.final <- list(
  edge.cut = 32 / 40,
  init.iterations = 0,
  init.temperature = 50,
  init.attraction = .5,
  init.damping.mult = 0,
  liquid.iterations = 0,
  liquid.temperature = 2000,
  liquid.attraction = 2,
  liquid.damping.mult = 1.0,
  expansion.iterations = 50,
  expansion.temperature = 2000,
  expansion.attraction = 2,
  expansion.damping.mult = 1.0,
  cooldown.iterations = 50,
  cooldown.temperature = 200,
  cooldown.attraction = 1,
  cooldown.damping.mult = .1,
  crunch.iterations = 50,
  crunch.temperature = 250,
  crunch.attraction = 1,
  crunch.damping.mult = 0.25,
  simmer.iterations = 25,
  simmer.temperature = 250,
  simmer.attraction = .5,
  simmer.damping.mult = 0
)

#' @family layout_drl
#' @export
drl_defaults <- list(
  coarsen = igraph.drl.coarsen,
  coarsest = igraph.drl.coarsest,
  default = igraph.drl.default,
  final = igraph.drl.final,
  refine = igraph.drl.refine
)

#' Align a vertex layout
#' This function centers a vertex layout on the coordinate system origin and
#' rotates the layout to achieve a visually pleasing alignment with the coordinate
#' axes. Doing this is particularly useful with force-directed layouts such as [layout_with_fr()].
#' @param graph The graph whose layout is to be aligned.
#' @param layout A matrix whose rows are the coordinates of vertices.
#' @return modified layout matrix
#' @export
#' @examples
#' g <- make_lattice(c(3, 3))
#' l1 <- layout_with_fr(g)
#' l2 <- align_layout(g,l1)
#' plot(g, layout = l1)
#' plot(g, layout = l2)
align_layout <- function(graph, layout) {
  layout_align_impl(
    graph = graph,
    layout = layout
  )
}
