(Plots.jl) aspect_ratio=1.0 for 3d plot

Issue

I don’t know how to set aspect_ratio=:equal for 3d plot (like axis_equal in MATLAB).
It seems that only x and y axes’ aspect ratio are equal.

Example code

using Plots


function test()
    N = 10
    xs = range(0, stop=10, length=N)
    ys = range(0, stop=20, length=N)
    zs = range(0, stop=50, length=N)
    p = plot(; aspect_ratio=1.0)
    plot!(xs, ys, zs)
end
test()

Result

@iHany, see revised improved solution using gr() backend below.

using Plots; gr(size=(600,600))

function plot_iso3d(xs, ys, zs; lw=3, lc=:red, title="Isometric 3D plot",label=false, camera=(45,30))
    # condition data for nearly isometric 3D plot 
    x12, y12, z12 = extrema(xs), extrema(ys), extrema(zs)
    d = maximum([diff([x12...]),diff([y12...]),diff([z12...])])[1] / 2
    xm, ym, zm = mean(x12),  mean(y12),  mean(z12) 

    # plot data
    p = Plots.plot(; xlabel="x",ylabel="y",zlabel="z", aspect_ratio=:equal, grid=:true)
    Plots.plot!(xlims=(xm-d,xm+d), ylims=(ym-d,ym+d), zlims=(zm-d,zm+d))
    Plots.plot!(;camera=camera)    #(azimuth,elevation) ???
    Plots.plot!(xs, ys, zs, title=title,lw=lw,lc=lc,label=label)
    Plots.plot!(xs, ys, zlims(p)[1] .+ 0*zs, lw=1, lc=:lightgray, label=false)
    Plots.plot!(xs, ylims(p)[2]  .+ 0*ys, zs, lw=1, lc=:lightgray, label=false)
    Plots.plot!(xlims(p)[1]  .+ 0*xs, ys, zs, lw=1, lc=:lightgray, label=false)
end

#input data
N = 100
xs = LinRange(0,40,N)
ys = LinRange(0,20,N) .+ 10*sin.(xs)
zs = LinRange(-10,50,N) .+ 15*sin.(xs)
plot_iso3d(xs, ys, zs)

2 Likes

Minor addition: one thing that always bothered me about doing this sort of thing in Matlab was that it wouldn’t actually do the exact same thing as automatic axis limits, because it didn’t expose the limit widening algorithm.

In Julia,

plot(x, y; xlims=Plots.widen(xmin, xmax))

gets rid of that problem. Just a bit of added sugar.

Oh no, it’s the best? Seriously?

It works well but is quite uncomfortable :frowning:

Anyhow, thanks :slight_smile:

“improved” != best
:slight_smile:

There were related questions and PRs in github, etc…, the best solution is in the hands of Plots.jl code developers.

NB: The road ahead is straight but the slope is steep…

1 Like

@iHany, updated iso plot 3d above, with shadow projections added.

Hi, I think that the key line of @rafael.guerra’s solution is:

using Plots; gr(size=(600,600))

At least at first sight, it seems that the attribute aspect_ratio=1.0 (or aspect_ratio=:equal) does work for 3d, but assumes that the figure canvas has square proportions – which is not true by default.

If you don’t want to reset the default figure size, you can also set it for a particular plot:

plot(x,y,z, aspect_ratio=1.0, size=(600,600))

@heliosdrm, the key lines in the solution posted above lie immediately below the comment:

# condition data for nearly isometric 3D plot 
...

Maybe I’m understanding a different point from @iHany 's original question? I interpreted that the problem was that the Z axis in his example looked squashed with respect to the X and Y axes, in spite of having set the attribute aspect_ratio=1.0. My point was that the only that has to be done to see the Z axis with the desired proportion is adding size=(600,600) in the call to either gr or plot.

(Edit: or size=(n,n), for whatever n is wanted).

That is obviously not enough, and will be data dependent.
For the original example given by OP, it will produce the plot below which is far from having aspect ratio = 1:

1 Like

You are right: silly oversight from my side. :blush:

1 Like