Does anyone know how to make a 3D plot of surfaces which are parametric? I mean e.g. sphere or Möbius strip. Looking at help and my own experiments, function surface draws on rectangles seem to not provide option of giving x and y as 2D matrices. However some old discussions, e.g.
suggest that this option was available some time ago. Now surface function allows to provide x,y,z as lists, but forcing them to make correct mesh seems to be quite unpredictable.
I’ve done this in Makie, but I am curious if I missed some option in Plots.
I have this plot recipe which was working last I checked, but only with the two backends. I don’t know how the rendering would work with more complicated surfaces, but cones and spheres work fine with pyplot.
"""
plot_parametric_surface(F, [xlim=(-5,5)], [ylim=(-5,5)], [nx=50], [ny=50])
Plot the parameterized surface described by `F:R^2 -> R^3`. This works with `pyplot` and `plotly`, but not `gr`.
A surface `(x,y,f(x,y))` can be directly plotted with `surface` as:
surface(xs, ys, f)
where `xs` and `ys` provide a grid to plot over.
For parametrically described surfaces, the above doesn't work. This function provides an interface:
F(u,v) = [u*cos(v), u*sin(v), u] # a cone
plot_parametric_surface(F, xlim=(0, 1), ylim=(0,2pi))
The values of `xlim` are used as the range to plot over; `nx` specifies the number of points to use. Similarly for `ylim`.
"""
@userplot Plot_Parametric_Surface
@recipe function __(r::Plot_Parametric_Surface; nx=50, ny=50)
F = first(r.args)
xlim = get(plotattributes,:xlims, (-5,5))
ylim = get(plotattributes,:ylims, (-5,5))
us = range(xlim[1], xlim[2], length=nx)
vs = range(ylim[1], ylim[2], length=ny)
ws = unzip(F.(us, vs'))
seriestype := :surface
x := ws[1]
y := ws[2]
z := ws[3]
xlims := extrema(ws[1])
ylims := extrema(ws[2])
ws
end
unzip(vs::Vector) = Tuple([[vs[i][j] for i in eachindex(vs)] for j in eachindex(vs[1])])
unzip(v,vs...) = unzip([v, vs...])
This code seems similar to my (failed) approach. However, for me it does not work at all. " Couldn’t process recipe args" and the rest of the error is gibberish. I do not have experience with user recipes, so I am not sure what is wrong here.
It doesn’t work properly. It has the same problem as my attempts - the mesh is off. This is not visible with this many points, but change to lenght = 20 and you will see this is not a sphere, this is some sort of ball shaped zig zag.
Thought maybe the plot_parametric_surface function from SymPy might help here, but you can try this and see it also does quite poorly:
using SymPy, PyPlot
@vars u v
SymPy.plot_parametric_surface((sin(u)*sin(v),cos(u)*sin(v), cos(v)), (u,0,2pi), (v,0,2pi)) # ok
SymPy.plot_parametric_surface((sin(u)*sin(v),cos(u)*sin(v), cos(v)), (u,0,2pi), (v,0,2pi), nb_of_points_u=10, nb_of_points_v=10) # not even a hint of a sphere...
using Plots
plotly() # or pyplot() - gr() does not work for me
# spherical: (radius r, inclination θ, azimuth φ)
X(r,theta,phi) = r * sin(theta) * sin(phi)
Y(r,theta,phi) = r * sin(theta) * cos(phi)
Z(r,theta,phi) = r * cos(theta)
thetas = range(0, stop=pi, length=50)
phis = range(0, stop=pi/2, length=50)
xs = [X(1, theta, phi) for theta in thetas, phi in phis]
ys = [Y(1, theta, phi) for theta in thetas, phi in phis]
zs = [Z(1, theta, phi) for theta in thetas, phi in phis]
surface(xs, ys, zs)
For me it doesn’t work in GR as well, in old versions it didn’t throw and error, but the result was wrong, now it throws an error (if I remember correctly, I don’t have Julia 1.3 now). So I guess we should just use new versions of Plots with plotly for parametric surfaces.
Thank you for this example! One thing I don’t understand is why do we need to transpose vs? And do you have any good resource to learn about how to make these plots?
I’m not sure of a good resource beyond the documentation of the plotting package for these plots specifically. Other plotting packages outside of Julia may create matrices for the u and v using a call to meshgrid, but this isn’t necessary for Makie or Plots, where the u and v values may be a vector and the f(u,v) values a matrix.