Streamplots with continuous lines in Makie

Hey. I’m trying to draw some streamplots (following this guide) but the orbits look somewhat broken and randomly put together. I’d like to have some more control over where the orbits begin and for how long they go, but the docs for streamplot are quite sparse, and I can’t quite figure out how to do it.

My code and the resulting plots:

using CairoMakie

x_lim = (-3,3)
y_lim = (-3,3)

stableVanDerPaul(x, y) = Point2f(y, (1 - x^2) * y - x)
semiStable(x, y) = Point2f(y, y - x)
titles = ["non-stable", "stable", "semi-stable"]
functions = [stableVanDerPaul, semiStable]
n = length(functions)
cmaps = [:gnuplot2, :gnuplot2, :gnuplot2]

fig = Figure(size = (800, 600), fontsize = 24)
axs = [Axis(fig[1, i], xlabel = "x", ylabel = "y", title = titles[i],
    aspect = 1, backgroundcolor = :white) for i in 1:n]
[streamplot!(axs[i], functions[i], -4 .. 4, -4 .. 4, colormap = [:red,:orange,:brown],
    gridsize = (32, 32), arrow_size = 10) for i in 1:n, density = 0.1]
[hideydecorations!(axs[2], grid = false, ticks = false) for i in 2:n]
[limits!(axs[i], x_lim...,y_lim...) for i in 1:n]
fig

image

Any advice? I’d be willing to use another package if necessary.

We could definitely use better docs for streamplot :smiley:

But to solve your question: there are four parameters (provided as keyword arguments) in streamplot which control how the output looks. These are:

  • gridsize: controls the number of “cells” in the grid, i.e., where a streamplot trajectory can start
  • maxsteps: what it says on the tin, the max number of steps that a simulation can go forward
  • density: the density of trajectories in the streamplot
  • stepsize: the size of each simulation step when simulating the trajectory

For our purposes here, we’ll ignore stepsize since it seems to be at least correct.

The beauty of Makie is that all these attributes are actually Observables, meaning that we can update them on the fly and Makie will automatically re-compute! You can use Makie’s UI functionality (sliders in this case) to allow you to interactively explore and understand what these parameters do (though we should document that as well!)

I made a small example to show how you can interactively manipulate these, and it ends at something which looks close to what you want. If you’d like to contribute to the streamplot docs / examples in Makie (after cleaning up the explanation a bit :D) please feel free!

using GLMakie
# GLMakie is the interactive backend, you could use WGLMakie as well, but not CairoMakie since we want to interact with the plot!
# First, your code - this is a direct copy + paste

x_lim = (-3,3)
y_lim = (-3,3)

stableVanDerPaul(x, y) = Point2f(y, (1 - x^2) * y - x)
semiStable(x, y) = Point2f(y, y - x)
titles = ["non-stable", "stable", "semi-stable"]
functions = [stableVanDerPaul, semiStable]
n = length(functions)
cmaps = [:gnuplot2, :gnuplot2, :gnuplot2]
fig = Figure(size = (800, 600), fontsize = 24)
axs = [Axis(fig[1, i], xlabel = "x", ylabel = "y", title = titles[i],
    aspect = 1, backgroundcolor = :white) for i in 1:n]
plts = [streamplot!(axs[i], functions[i], -4 .. 4, -4 .. 4, colormap = [:red,:orange,:brown],
    gridsize = (32, 32), arrow_size = 10) for i in 1:n, density = 0.1]
[hideydecorations!(axs[2], grid = false, ticks = false) for i in 2:n]
[limits!(axs[i], x_lim...,y_lim...) for i in 1:n]
fig

# now, we initialize a grid of sliders which allow us to control the parameters
sg = SliderGrid(
    fig[2, :],
    (label = "Quality", range = 5:40, startvalue = 16),
    (label = "Density", range = LinRange(0, 2, 40), startvalue = 1),
    (label = "Max steps", range = round.(Int, LinRange(250, 2000, 20)), startvalue = 500),
    (label = "Grid size", range = round.(Int, LinRange(16, 100, 20)), startvalue = 32),
)
# each slider has an observable called `value`, which updates whenever the slider is moved
# we pass these updates on to the plots!
on(sg.sliders[1].value) do quality
    plts[1].quality[] = quality
    plts[2].quality[] = quality
end

on(sg.sliders[2].value) do density
    plts[1].density[] = density
    plts[2].density[] = density
end

on(sg.sliders[3].value) do maxsteps
    plts[1].maxsteps[] = maxsteps
    plts[2].maxsteps[] = maxsteps
end

on(sg.sliders[4].value) do gridsize
    plts[1].gridsize[] = (gridsize, gridsize, gridsize)
    plts[2].gridsize[] = (gridsize, gridsize, gridsize)
end


fig
4 Likes

This should work for now, thank you very much! I’d love to contribute to makie’s docs, just let me finish up this paper first :).

1 Like