diff --git a/docs/make.jl b/docs/make.jl index 08d5c9c..8784066 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,8 +4,18 @@ using Documenter, EcologicalNetworksPlots const pages = [ "Index" => "index.md", - "Examples" => "examples.md", - "Reference" => "library.md" + "Layouts" => [ + "Introduction" => "layouts/initial.md", + "Circular" => "layouts/circular.md", + "Bipartite" => "layouts/bipartite.md", + "Force-directed" => "layouts/forcedirected.md", + "Unravel" => "layouts/unravelled.md" + ], + "Advanced topics" => [ + "Nodes attributes" => "advanced/attributes.md", + "Networks subsets" => "advanced/subsets.md", + ] + # TODO add plotting, heatmap, advanced uses ] makedocs( @@ -15,6 +25,6 @@ makedocs( ) deploydocs( - repo = "github.com/PoisotLab/EcologicalNetworksPlots.jl.git", + repo = "github.com/EcoJulia/EcologicalNetworksPlots.jl.git", push_preview = true ) diff --git a/docs/src/advanced/attributes.md b/docs/src/advanced/attributes.md new file mode 100644 index 0000000..b2ac764 --- /dev/null +++ b/docs/src/advanced/attributes.md @@ -0,0 +1,39 @@ +## Node color + +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +```@example default +Unes = web_of_life("M_SD_033") +I = initial(BipartiteInitialLayout, Unes) +position!(NestedBipartiteLayout(0.4), I, Unes) +plot(I, Unes, aspectratio=1) +scatter!(I, Unes, bipartite=true, nodefill=degree(Unes), c=:cividis) +``` + +## Node size + +The size of the nodes can be changed using the `nodesize` argument, which is a +dictionary mapping species to values. These values are scaled when making the +figures. Note that in this example we also label the number of the node. + +```@example default +Unes = web_of_life("M_SD_033") +I = initial(BipartiteInitialLayout, Unes) +position!(NestedBipartiteLayout(0.4), I, Unes) +plot(I, Unes, aspectratio=1) +scatter!(I, Unes, bipartite=true, nodesize=degree(Unes)) +``` + +## Node annotations + +```@example default +Unes = web_of_life("M_SD_033") +I = initial(BipartiteInitialLayout, Unes) +position!(NestedBipartiteLayout(0.4), I, Unes) +plot(I, Unes, aspectratio=1) +scatter!(I, Unes, bipartite=true, series_annotations = string.(1:richness(Unes))) +``` diff --git a/docs/src/advanced/subsets.md b/docs/src/advanced/subsets.md new file mode 100644 index 0000000..73928bf --- /dev/null +++ b/docs/src/advanced/subsets.md @@ -0,0 +1,23 @@ +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +One important feature of the package is that the layout can contain *more* nodes +than the network. For example, we can use this to our advantage, to represent +species with a degree larger than 3 in red: + +```@example default +Umod = web_of_life("M_PA_003") +I = initial(RandomInitialLayout, Umod) +for step in 1:4000 + position!(ForceDirectedLayout(2.5, 0.4), I, Umod) +end +plot(I, Umod, aspectratio=1) +scatter!(I, Umod) +N = convert(AbstractUnipartiteNetwork, convert(BinaryNetwork, Umod)) +core3 = collect(keys(filter(p -> p.second == 3, degree(N)))) +plot!(I, N[core3], lc=:red) +scatter!(I, N[core3], mc=:red) +``` diff --git a/docs/src/assets/logo.png b/docs/src/assets/logo.png new file mode 100644 index 0000000..015109a Binary files /dev/null and b/docs/src/assets/logo.png differ diff --git a/docs/src/examples.md b/docs/src/examples.md deleted file mode 100644 index d40a98a..0000000 --- a/docs/src/examples.md +++ /dev/null @@ -1,163 +0,0 @@ -```@setup default -using EcologicalNetworks -using EcologicalNetworksPlots -using Plots -``` - -## Nested layout - -```@example default -Unes = web_of_life("M_SD_033") -I = initial(BipartiteInitialLayout, Unes) -position!(NestedBipartiteLayout(0.4), I, Unes) -plot(I, Unes, aspectratio=1) -scatter!(I, Unes, bipartite=true) -``` - -## Circular layout - -```@example default -Unes = web_of_life("M_SD_033") -I = initial(CircularInitialLayout, Unes) -position!(CircularLayout(), I, Unes) -plot(I, Unes, aspectratio=1) -scatter!(I, Unes, bipartite=true) -``` - -## Force directed layout - -```@example default -Umod = web_of_life("M_PA_003") -I = initial(RandomInitialLayout, Umod) -for step in 1:2000 - position!(ForceDirectedLayout(1.5), I, Umod) -end -plot(I, Umod, aspectratio=1) -scatter!(I, Umod, bipartite=true) -``` - -## Food web layout - -```@example default -Fweb = simplify(nz_stream_foodweb()[5]) -I = initial(FoodwebInitialLayout, Fweb) -for step in 1:4000 - position!(ForceDirectedLayout(true, false, 2.5), I, Fweb) -end -plot(I, Fweb) -scatter!(I, Fweb) -``` - -Note that we can replace some properties of the nodes in the layout *after* the -positioning algorithm occurred -- so we can, for example, use the actual -(instead of fractional) trophic level: - -```@example default -Fweb = simplify(nz_stream_foodweb()[5]) -I = initial(FoodwebInitialLayout, Fweb) -for step in 1:4000 - position!(ForceDirectedLayout(true, false, 2.5), I, Fweb) -end -tl = trophic_level(Fweb) -for s in species(Fweb) - I[s].y = tl[s] -end -plot(I, Fweb) -scatter!(I, Fweb) -``` - -## Node properties - -### Color - -```@example default -Unes = web_of_life("M_SD_033") -I = initial(BipartiteInitialLayout, Unes) -position!(NestedBipartiteLayout(0.4), I, Unes) -plot(I, Unes, aspectratio=1) -scatter!(I, Unes, bipartite=true, nodefill=degree(Unes)) -``` - -### Size - -The size of the nodes can be changed using the `nodesize` argument, which is a -dictionary mapping species to values. These values are scaled when making the -figures. Note that in this example we also label the number of the node. - -```@example default -Unes = web_of_life("M_SD_033") -I = initial(BipartiteInitialLayout, Unes) -position!(NestedBipartiteLayout(0.4), I, Unes) -plot(I, Unes, aspectratio=1) -scatter!(I, Unes, bipartite=true, nodesize=degree(Unes), series_annotations = string.(1:richness(Unes))) -``` - -## Network subsets - -One important feature of the package is that the layout can contain *more* nodes -than the network. For example, we can use this to our advantage, to represent -species with a degree larger than 3 in red: - -```@example default -Umod = web_of_life("M_PA_003") -I = initial(RandomInitialLayout, Umod) -for step in 1:4000 - position!(ForceDirectedLayout(2.5), I, Umod) -end -plot(I, Umod, aspectratio=1) -scatter!(I, Umod) -N = convert(AbstractUnipartiteNetwork, convert(BinaryNetwork, Umod)) -core3 = collect(keys(filter(p -> p.second == 3, degree(N)))) -plot!(I, N[core3], lc=:red) -scatter!(I, N[core3], mc=:red) -``` - -## Heatmap - -```@example default -Umod = web_of_life("M_PA_003") -heatmap(Umod, c=:YlGnBu) -``` - -```@example default -Umod = convert(BipartiteNetwork, web_of_life("M_PA_003")) -heatmap(convert(UnipartiteNetwork, Umod)) -``` - -## Unravelled layout - -The unravelled layout is essentially a scatterplot of network properties with -interactions drawn as well. This is inspired by [the work of Giulio Valentina -Dalla Riva on this visualisation][gvdr]. By default, it will compare the -omnivory index and the trophic level: - -[gvdr]: https://github.com/gvdr/unravel - -```@example default -N = nz_stream_foodweb()[10] -I = initial(UnravelledInitialLayout, N) -plot(I, N, lab="", framestyle=:box) -scatter!(I, N, nodefill=degree(N), colorbar=true, framestyle=:box) -``` - -Because a lot of species will have the same omnivory index, we might want to use -a slightly different function, which adds some randomness to the omnivory: - -```@example default -N = nz_stream_foodweb()[10] -I = initial(UnravelledInitialLayout, N) - -function random_omnivory(N::T) where {T <: UnipartiteNetwork} - o = omnivory(N) - for s in species(N) - o[s] += (rand()-0.5)*0.1 - end - return o -end - -UL = UnravelledLayout(x=random_omnivory, y=trophic_level) -position!(UL, I, N) - -plot(I, N, lab="", framestyle=:box) -scatter!(I, N, nodefill=degree(N), colorbar=true, framestyle=:box, mc=:viridis) -``` diff --git a/docs/src/index.md b/docs/src/index.md index bc1f27e..ec7b2f2 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -10,10 +10,9 @@ The second option requires to set a layout, of which there are multiple types according to the type of network, the type of layout, and the information to emphasize. Applying a layout consists of a call to `initial`, followed by one or more calls to `position!`. The *nodes* in the network are represented using -`scatter`, and the *links* using `plot`. - -Probabilistic networks have link *probability* denoted as transparency, and -quantitative network have link *strength* represented as width. +`scatter`, and the *links* using `plot`. Probabilistic networks have link +*probability* denoted as transparency, and quantitative network have link +*strength* represented as width. Both the fill and color of the nodes can be changed, using the `nodefill` and `nodesize` arguments -- these must be dictionaries mapping *all nodes* in @@ -21,3 +20,10 @@ the network to a single numerical value, and they affect the `markerfill` and `markerz` value of `Plots`, respectively. Note that by default, `frametype` is `:none` and `legend` is `false`, but this can be changed. It is particularly important to change it for `UnravelledLayout`, for example. + +An important point, which can be used to create complex visualisations, is that +you can call the `scatter` and `plot` functions on dictionaries of positions +that have *more* points that are in the network. This can, among other things, +allow you to use different colormaps for the degree of different nodes, or color +different sub-graphs in the network. There are a few example of these uses in +the documentation. diff --git a/docs/src/layouts/bipartite.md b/docs/src/layouts/bipartite.md new file mode 100644 index 0000000..a742916 --- /dev/null +++ b/docs/src/layouts/bipartite.md @@ -0,0 +1,22 @@ +## Layouts + +```@docs +BipartiteInitialLayout +NestedBipartiteLayout +``` + +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +## Example + +```@example default +Unes = web_of_life("M_SD_033") +I = initial(BipartiteInitialLayout, Unes) +position!(NestedBipartiteLayout(0.4), I, Unes) +plot(I, Unes, aspectratio=1) +scatter!(I, Unes, bipartite=true) +``` \ No newline at end of file diff --git a/docs/src/layouts/circular.md b/docs/src/layouts/circular.md new file mode 100644 index 0000000..4ad0d7b --- /dev/null +++ b/docs/src/layouts/circular.md @@ -0,0 +1,22 @@ +## Layouts + +```@docs +CircularInitialLayout +CircularLayout +``` + +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +## Example + +```@example default +Unes = web_of_life("M_SD_033") +I = initial(CircularInitialLayout, Unes) +position!(CircularLayout(), I, Unes) +plot(I, Unes, aspectratio=1) +scatter!(I, Unes, bipartite=true) +``` \ No newline at end of file diff --git a/docs/src/layouts/forcedirected.md b/docs/src/layouts/forcedirected.md new file mode 100644 index 0000000..5949a04 --- /dev/null +++ b/docs/src/layouts/forcedirected.md @@ -0,0 +1,75 @@ +## Layouts + +```@docs +FoodwebInitialLayout +RandomInitialLayout +ForceDirectedLayout +``` + + +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +## Bipartite example + +In this example, we have a quantitative bipartite network, and we will set no +gravity (nodes can move as far away as they want from the center). Note that our +initial layout is a `RandomInitialLayout`, but we can use *any* layout we see +fit when starting. + +```@example default +N = web_of_life("M_PA_003") +I = initial(RandomInitialLayout, N) +plot(I, N, aspectratio=1) +scatter!(I, N, bipartite=true) +``` + +The next step is to actually position the nodes relative to one another. Because +this network has disconnected components, and we have no gravity, we expect that +they will be quite far from one another: + +```@example default +for step in 1:2000 + position!(ForceDirectedLayout(0.3, 0.3; gravity=0.0), I, N) +end +plot(I, N, aspectratio=1) +scatter!(I, N, bipartite=true) +``` + +We can turn gravity on just a little bit: + +```@example default +I = initial(RandomInitialLayout, N) +for step in 1:2000 + position!(ForceDirectedLayout(0.3, 0.3; gravity=0.2), I, N) +end +plot(I, N, aspectratio=1) +scatter!(I, N, bipartite=true) +``` + +We can also make links attract more strongly than nodes repel (and turn gravity +on some more): + +```@example default +I = initial(RandomInitialLayout, N) +for step in 1:2000 + position!(ForceDirectedLayout(0.3, 0.75; gravity=0.4), I, N) +end +plot(I, N, aspectratio=1) +scatter!(I, N, bipartite=true) +``` + +## Food web example + +```@example default +Fweb = simplify(nz_stream_foodweb()[5]) +I = initial(FoodwebInitialLayout, Fweb) +for step in 1:4000 + position!(ForceDirectedLayout((true, false), (0.15, 0.35), 0.05), I, Fweb) +end +plot(I, Fweb) +scatter!(I, Fweb) +``` \ No newline at end of file diff --git a/docs/src/layouts/initial.md b/docs/src/layouts/initial.md new file mode 100644 index 0000000..33f5c60 --- /dev/null +++ b/docs/src/layouts/initial.md @@ -0,0 +1,19 @@ +## Creating the initial layout + +The first step is to generate a starting position for the nodes in the network. +In a lot of cases, this is a pseudo-random position, which is then refined. The +methods for every layouts have different initial conditions. + +```@docs +initial +``` + +## Applying the layout + +The second step is to apply the layout. In most cases, this only needs to be +done once. Force-directed layouts can require a very large number of iterations, +and also tend to scale very poorly with the size of the network. + +```@docs +position! +``` \ No newline at end of file diff --git a/docs/src/layouts/unravelled.md b/docs/src/layouts/unravelled.md new file mode 100644 index 0000000..a587dcb --- /dev/null +++ b/docs/src/layouts/unravelled.md @@ -0,0 +1,48 @@ +The unravelled layout is essentially a scatterplot of network properties with +interactions drawn as well. This is inspired by [the work of Giulio V. Dalla +Riva on this visualisation](https://github.com/gvdr/unravel). By default, it +will compare the omnivory index and the trophic level: + +## Layouts + +```@docs +UnravelledInitialLayout +UnravelledLayout +``` + +## Example + +```@setup default +using EcologicalNetworks +using EcologicalNetworksPlots +using Plots +``` + +```@example default +N = nz_stream_foodweb()[10] +I = initial(UnravelledInitialLayout, N) +plot(I, N, lab="", framestyle=:box) +scatter!(I, N, nodefill=degree(N), colorbar=true, framestyle=:box) +``` + +Because a lot of species will have the same omnivory index, we might want to use +a slightly different function, which adds some randomness to the omnivory: + +```@example default +N = nz_stream_foodweb()[10] +I = initial(UnravelledInitialLayout, N) + +function random_omnivory(N::T) where {T <: UnipartiteNetwork} + o = omnivory(N) + for s in species(N) + o[s] += (rand()-0.5)*0.1 + end + return o +end + +UL = UnravelledLayout(x=random_omnivory, y=trophic_level) +position!(UL, I, N) + +plot(I, N, lab="", framestyle=:box) +scatter!(I, N, nodefill=degree(N), colorbar=true, framestyle=:box, mc=:viridis) +``` diff --git a/docs/src/library.md b/docs/src/library.md deleted file mode 100644 index a7c1b22..0000000 --- a/docs/src/library.md +++ /dev/null @@ -1,37 +0,0 @@ -## Prepare and apply a layout - -```@docs -initial -position! -``` - -## Layouts - -### Force-directed - -```@docs -FoodwebInitialLayout -RandomInitialLayout -ForceDirectedLayout -``` - -### Circular - -```@docs -CircularInitialLayout -CircularLayout -``` - -### Nested (bipartite only) - -```@docs -BipartiteInitialLayout -NestedBipartiteLayout -``` - -### Unravelled - -```@docs -UnravelledInitialLayout -UnravelledLayout -``` diff --git a/src/forcedirected.jl b/src/forcedirected.jl index 061ea50..4229ab9 100644 --- a/src/forcedirected.jl +++ b/src/forcedirected.jl @@ -3,96 +3,81 @@ The fields are, in order: -- `move_x`, to specificy if the nodes are allowed to move horizontally -- `move_y`, to specificy if the nodes are allowed to move vertically -- `k`, the spring coefficient, set to `0.2` by default in most cases -- `center`, to specify if the nodes are pulled towards the center -- `height`, the height of the space, set to `1.0` by default +- `move`, a tuple to specify whether moves on the x and y axes are allowed +- `k`, a tuple (kₐ,kᵣ) giving the strength of attraction and repulsion +- `gravity`, the strength of attraction towards the center, set to `0.0` as a default The spring coefficient is used to decide how strongly nodes will *attract* or *repel* one another, as a function of their distance Δ. Specifically, the -default is that connected nodes will attract one another proportionally to Δ²/k, -and all nodes will repel one another proportionally to k²/Δ. - -If `center=true`, the nodes are *all* attracted to the center at a strength -proportional to 75% of the attraction they would have from a connected node. +default is that connected nodes will attract one another proportionally to Δ²/kₐ, +and all nodes will repel one another proportionally to kᵣ²/Δ. """ struct ForceDirectedLayout - move_x::Bool - move_y::Bool - k::Float64 - center::Bool - height::Float64 + move::Tuple{Bool,Bool} + k::Tuple{Float64,Float64} + gravity::Float64 end """ - ForceDirectedLayout(;k::Bool=0.2, center::Bool=true) + ForceDirectedLayout(ka::Float64, kr::Float64; gravity::Float64=0.75) -Creates a default force directed layout where nodes move in both directions, are -attracted to the center, with a spring coefficient of 0.2. The spring -coefficient can be changed with the `k` argument, and the attachment to the -center can be changed with the `center` keyword. Note that if the network as -multiple disconnected components, `center=false` can lead to strange results. +TODO """ -ForceDirectedLayout(;k::Float64=0.2, center::Bool=true) = ForceDirectedLayout(true, true, k, center, 1.0) - -ForceDirectedLayout(k::Float64) = ForceDirectedLayout(true, true, k, true, 1.0) -ForceDirectedLayout(mx::Bool, my::Bool) = ForceDirectedLayout(mx, my, 0.2, true, 1.0) -ForceDirectedLayout(mx::Bool, my::Bool, k::Float64) = ForceDirectedLayout(mx, my, k, true, 1.0) +ForceDirectedLayout(ka::Float64, kr::Float64; gravity::Float64=0.75) = ForceDirectedLayout((true,true), (ka,kr), gravity) """ Stops the movement of a node position. """ function stop!(n::NodePosition) - n.vx = 0.0 - n.vy = 0.0 + n.vx = 0.0 + n.vy = 0.0 end """ Repel two nodes """ function repel!(LA::T, n1::NodePosition, n2::NodePosition, fr) where {T <: ForceDirectedLayout} - δx = n1.x - n2.x - δy = n1.y - n2.y - Δ = sqrt(δx^2.0+δy^2.0) - Δ = Δ == 0.0 ? 0.0001 : Δ - if LA.move_x - n1.vx = n1.vx + δx/Δ*fr(Δ) - n2.vx = n2.vx - δx/Δ*fr(Δ) - end - if LA.move_y # Do we need to move y here? - n1.vy = n1.vy + δy/Δ*fr(Δ) - n2.vy = n2.vy - δy/Δ*fr(Δ) - end + δx = n1.x - n2.x + δy = n1.y - n2.y + Δ = sqrt(δx^2.0+δy^2.0) + Δ = Δ == 0.0 ? 0.0001 : Δ + if LA.move[1] + n1.vx = n1.vx + δx/Δ*fr(Δ) + n2.vx = n2.vx - δx/Δ*fr(Δ) + end + if LA.move[2] # Do we need to move y here? + n1.vy = n1.vy + δy/Δ*fr(Δ) + n2.vy = n2.vy - δy/Δ*fr(Δ) + end end """ Attract two connected nodes """ function attract!(LA::T, n1::NodePosition, n2::NodePosition, fa) where {T <: ForceDirectedLayout} - δx = n1.x - n2.x - δy = n1.y - n2.y - Δ = sqrt(δx^2.0+δy^2.0) - Δ = Δ == 0.0 ? 0.0001 : Δ - if LA.move_x - n1.vx = n1.vx - δx/Δ*fa(Δ) - n2.vx = n2.vx + δx/Δ*fa(Δ) - end - if LA.move_y - n1.vy = n1.vy - δy/Δ*fa(Δ) - n2.vy = n2.vy + δy/Δ*fa(Δ) - end + δx = n1.x - n2.x + δy = n1.y - n2.y + Δ = sqrt(δx^2.0+δy^2.0) + Δ = Δ == 0.0 ? 0.0001 : Δ + if LA.move[1] + n1.vx = n1.vx - δx/Δ*fa(Δ) + n2.vx = n2.vx + δx/Δ*fa(Δ) + end + if LA.move[2] + n1.vy = n1.vy - δy/Δ*fa(Δ) + n2.vy = n2.vy + δy/Δ*fa(Δ) + end end """ Update the position of a node """ -function update!(LA::T, n::NodePosition) where {T <: ForceDirectedLayout} - Δ = sqrt(n.vx^2.0+n.vy^2.0) - Δ = Δ == 0.0 ? 0.0001 : Δ - n.x += n.vx/Δ*min(Δ, 0.01) - n.y += n.vy/Δ*min(Δ, 0.01) - stop!(n) +function update!(n::NodePosition) where {T <: ForceDirectedLayout} + Δ = sqrt(n.vx^2.0+n.vy^2.0) + Δ = Δ == 0.0 ? 0.0001 : Δ + n.x += n.vx/Δ*min(Δ, 0.01) + n.y += n.vy/Δ*min(Δ, 0.01) + stop!(n) end """ @@ -103,33 +88,26 @@ take some time to converge, it may be useful to stop every 500 iterations to have a look at the results. """ function position!(LA::ForceDirectedLayout, L::Dict{K,NodePosition}, N::T) where {T <: EcologicalNetworks.AbstractEcologicalNetwork} where {K} - fa(x) = (x*x)/LA.k # Default attraction function - fr(x) = (LA.k*LA.k)/x # Default repulsion function - for (i, s1) in enumerate(species(N)) - n1 = L[s1] - stop!(n1) - for (j, s2) in enumerate(species(N)) - n2 = L[s2] - if j != i - repel!(LA, n1, n2, fr) - end - end - end + fa(x) = (x^2.0)/LA.k[1] # Default attraction function + fr(x) = (LA.k[2]^2.0)/x # Default repulsion function + + plotcenter = NodePosition(0.0, 0.0, 0.0, 0.0) - for int in interactions(N) - n1, n2 = L[int.from], L[int.to] - attract!(LA, n1, n2, fa) - end + for (i, s1) in enumerate(species(N)) + attract!(LA, L[s1], plotcenter, (x) -> LA.gravity*fa(x)) + for (j, s2) in enumerate(species(N)) + if j > i + repel!(LA, L[s1], L[s2], fr) + end + end + end + + for int in interactions(N) + attract!(LA, L[int.from], L[int.to], fa) + end - if LA.center - plotcenter = NodePosition(0.0, 0.0, 0.0, 0.0) for s in species(N) - attract!(LA, L[s], plotcenter, (x) -> 0.75*fa(x)) + update!(L[s]) end - end - - for s in species(N) - update!(LA, L[s]) - end - + end diff --git a/src/types.jl b/src/types.jl index 5a6f455..4ad0487 100644 --- a/src/types.jl +++ b/src/types.jl @@ -13,7 +13,13 @@ mutable struct NodePosition r::Number end -NodePosition() = NodePosition(rand(), rand(), 0.0, 0.0, 0.0) +function NodePosition() + n1, n2 = (rand(2).*2.0).-1.0 + while ((n1^2.0)+(n2^2.0))≥1.0 + n1, n2 = (rand(2).*2.0).-1.0 + end + NodePosition(n1, n2, 0.0, 0.0, 0.0) +end NodePosition(x::Float64, y::Float64) = NodePosition(x, y, 0.0, 0.0, 0.0) NodePosition(x::Float64, y::Float64, vx::Float64, vy::Float64) = NodePosition(x, y, vx, vy, 0.0) @@ -46,7 +52,7 @@ struct FoodwebInitialLayout end RandomInitialLayout This type is used to generate an initial layout, where the nodes are -placed at random. +placed at random within the unit circle. """ struct RandomInitialLayout end diff --git a/test/runtests.jl b/test/runtests.jl index ed89133..8ab3641 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -51,7 +51,7 @@ scatter!(I, Unes, bipartite=true, nodefill=degree(Unes)) Fweb = simplify(nz_stream_foodweb()[5]) I = initial(FoodwebInitialLayout, Fweb) for step in 1:300 - position!(ForceDirectedLayout(true, false, 2.5), I, Fweb) + position!(ForceDirectedLayout((true, false), (2.5, 1.5), 0.7), I, Fweb) end tl = trophic_level(Fweb) for s in species(Fweb)