# Drawing parametric surfaces in Plots.jl

**URL:** <https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560>\
**Category:** General Usage\
**Created:** [March 26, 2020, 5:25pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560 "2020-03-26T17:25:19Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbl/32/5984_2.png) [@Tbl](https://discourse.julialang.org/u/Tbl)\
**Post date:** [March 26, 2020, 5:25pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/1 "2020-03-26T17:25:20Z")

</div>

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.

[https://stackoverflow.com/questions/44608878/plots-jl-turn-off-axis-and-grid-lines](https://stackoverflow.com/questions/44608878/plots-jl-turn-off-axis-and-grid-lines)

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.

Julia v1.3.1, Plots v0.29.8

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [March 26, 2020, 7:36pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/2 "2020-03-26T19:36:09Z")

</div>

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.

```julia
"""
    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...])

```

---

<div class="post-metadata">

**Author:** ![Tbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbl/32/5984_2.png) [@Tbl](https://discourse.julialang.org/u/Tbl)\
**Post date:** [March 26, 2020, 8:03pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/3 "2020-03-26T20:03:21Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [March 26, 2020, 8:54pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/4 "2020-03-26T20:54:55Z")

</div>

Sorry, I should have unpacked the recipe. Something like this should work, using the `unzip` function defined previously:

```julia
F(u,v) = [cos(u)*sin(v), sin(u)*sin(v), cos(v)]
vs = us = range(0,2pi, length=100)
surface(unzip(F.(us, vs')...))

```

---

<div class="post-metadata">

**Author:** ![Tbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbl/32/5984_2.png) [@Tbl](https://discourse.julialang.org/u/Tbl)\
**Post date:** [March 26, 2020, 9:31pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/5 "2020-03-26T21:31:02Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [March 26, 2020, 9:56pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/6 "2020-03-26T21:56:28Z")

</div>

Yeah, that’s right. Looks terrible with n=5. Sorry. I don’t have thoughts on fix for that, as I had only tested this for simpler surfaces.

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [March 26, 2020, 10:14pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/7 "2020-03-26T22:14:57Z")

</div>

Thought maybe the `plot_parametric_surface` function from `SymPy` might help here, but you can try this and see it also does quite poorly:

```julia
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...

```

---

<div class="post-metadata">

**Author:** ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)\
**Post date:** [March 27, 2020, 12:16am UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/8 "2020-03-27T00:16:10Z")

</div>

@bachrathyd has some nice 3D plots of implicit functions in [https://github.com/bachrathyd/MDBM.jl](https://github.com/bachrathyd/MDBM.jl)

IntervalConstraintPropagation.jl can so this by approximating the surface with boxes, which you can view with Makie.jl.

---

<div class="post-metadata">

**Author:** ![rrhiemstra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rrhiemstra/32/12263_2.png) [@rrhiemstra](https://discourse.julialang.org/u/rrhiemstra)\
**Post date:** [October 5, 2020, 11:59am UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/9 "2020-10-05T11:59:48Z")

</div>

```julia
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)

```

![surface](https://global.discourse-cdn.com/julialang/original/3X/5/d/5dbf787f1c1abde8b2445a936634e55c1f640773.png)

---

<div class="post-metadata">

**Author:** ![Tbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbl/32/5984_2.png) [@Tbl](https://discourse.julialang.org/u/Tbl)\
**Post date:** [October 5, 2020, 7:44pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/10 "2020-10-05T19:44:38Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [October 7, 2020, 2:23pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/11 "2020-10-07T14:23:56Z")

</div>

You can also use Makie, which does this quite nicely. Here is a different, but similar example:

```julia
using AbstractPlotting, GLMakie
r1, r2 = 2, 1/2
r(u,v) = ((r1 + r2*cos(v))*cos(u), (r1 + r2*cos(v))*sin(u), r2*sin(v)) # torus
us = vs = range(0, 2pi, length=25)
xs, ys, zs = [[pt[i] for pt in r.(us, vs')] for i in 1:3]
surface(xs, ys, zs)

```

---

<div class="post-metadata">

**Author:** ![Tbl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbl/32/5984_2.png) [@Tbl](https://discourse.julialang.org/u/Tbl)\
**Post date:** [October 7, 2020, 2:36pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/12 "2020-10-07T14:36:26Z")

</div>

I know, to quote my first post

> [@Tbl](#):
>
> I’ve done this in Makie, but I am curious if I missed some option in Plots.

🙂

---

<div class="post-metadata">

**Author:** ![MauroCE](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauroce/32/22056_2.png) [@MauroCE](https://discourse.julialang.org/u/MauroCE)\
**Post date:** [February 16, 2021, 12:48pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/13 "2021-02-16T12:48:54Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [February 16, 2021, 4:19pm UTC](https://discourse.julialang.org/t/drawing-parametric-surfaces-in-plots-jl/36560/14 "2021-02-16T16:19:46Z")

</div>

The transpose is used to create a grid of values as opposed to a vector when Julia’s broadcasting is used. This example might be instructive:

```julia
julia> ys = xs = -1:1
-1:1

julia> tuple.(xs, ys)
3-element Array{Tuple{Int64,Int64},1}:
 (-1, -1)
 (0, 0)
 (1, 1)

julia> tuple.(xs, ys')
3×3 Array{Tuple{Int64,Int64},2}:
 (-1, -1) (-1, 0) (-1, 1)
 (0, -1) (0, 0) (0, 1)
 (1, -1) (1, 0) (1, 1)

```

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.
