How to do multiple cameras in subplots with plot3d()

I’m using the Plots library to visualise some randomly generated gravitational motion in a gif. I generate a Vector{Vector{Vector{Float64}}}, which can be thought of as a list of frames, where each frame has a list of bodies, each of which is a list of 3 coordinates in 3D space. I can plot these with plot3d() and a loop to add the position of each body in its own series on the plot, and I can animate this with @gif.

My problem is that the camera angle makes it hard to see some of the more intricate 3D motions. It can be hard to tell where a body is, and whether it moves in front or behind another body. I have experimented with an oscillating camera angle using a cos function that takes the number of the current frame, but that distorted the plot and made it worse, in my opinion.

What I want is to have four separate subplots on the plot. The main one would be the default 3d camera, but then there would be three other subplots below it, showing orthogonal projections from each cardinal direction. I basically want the layout seen on the DifferentialEquations.jl animation page, but without the plot over time.
https://diffeq.sciml.ai/stable/basics/plot/#Example

But I’m not doing a differential equation, so I don’t have that sol object, and I’m trying to render it as a gif, so I think I need to put all the subplots in the same loop and add each frame to them, unless there’s an easy way of mapping my list of lists of lists onto frames in an animation.

I’m very new to Julia and don’t really understand subplots at all, especially not in an animation like this, so any help would be greatly appreciated.

Small working examples

For more details, see Jupyter notebook.

EDIT: For instructions on how to set up the subplot layout, see Layouts · Plots

The following code is copied from Plot Functions · DifferentialEquations.jl

using DifferentialEquations, Plots
function lorenz(du,u,p,t)
 du[1] = p[1]*(u[2]-u[1])
 du[2] = u[1]*(p[2]-u[3]) - u[2]
 du[3] = u[1]*u[2] - p[3]*u[3]
end

u0 = [1., 5., 10.]
tspan = (0., 100.)
p = (10.0,28.0,8/3)
prob = ODEProblem(lorenz, u0, tspan,p)
sol = solve(prob);

Create data of simple arrays for test plotting:

t = range(sol.prob.tspan...; length=10^4+1)
X, Y, Z = ((t -> sol(t)[i]).(t) for i in 1:3)
xlim, ylim, zlim = extrema.((X, Y, Z));

Animate 3D plottings with gr() backend:

gr(fmt = :png)
anim = @animate for i in 1:100:length(X)
    @views x, y, z = X[1:i], Y[1:i], Z[1:i]
    A = plot(x, y, z; label="", lw=0.5, xlim, ylim, zlim)
    B = plot(x, y; label="", lw=0.2, title="x, y", titlefontsize=8, xlim=xlim, ylim=ylim)
    C = plot(x, z; label="", lw=0.2, title="x, y", titlefontsize=8, xlim=xlim, ylim=zlim)
    D = plot(y, z; label="", lw=0.2, title="x, y", titlefontsize=8, xlim=ylim, ylim=zlim)
    layout = @layout [
        a{0.7h}
        [b c d]
    ]
    plot(A, B, C, D; layout, size=(640, 640))
end
gif(anim, "lorenz2.gif")

image

Animate rotated 3D plottings with pyplot() backend:

pyplot(fmt = :png)
anim = @animate for (k, i) in enumerate(1:50:length(X))
    @views x, y, z = X[1:i], Y[1:i], Z[1:i]
    A = plot(x, y, z; label="", lw=0.3, xlim, ylim, zlim, camera=(360k/100, 30))
    B = plot(x, y; label="", lw=0.15, title="x, y", titlefontsize=8, xlim=xlim, ylim=ylim)
    C = plot(x, z; label="", lw=0.15, title="x, z", titlefontsize=8, xlim=xlim, ylim=zlim)
    D = plot(y, z; label="", lw=0.15, title="y, z", titlefontsize=8, xlim=ylim, ylim=zlim)
    layout = @layout [a{0.75w} [b; c; d]]
    plot(A, B, C, D; layout, size=(720, 500))
end
PyPlot.clf()
gif(anim, "lorenz4.gif")

Small size version (full size version)
image

2 Likes