Use Plots() library to graph a colored surface

I want to make a surface in 3D and color the surface using a fourth coordinate. I want to do this task using Plots and Plotly to rotate the graph at all angles. Here I put examples of the surfaces I want:

im1
im2
im3

Just to double check, you don’t accept a solution with Makie.jl? BeautifulMakie has various examples: Beautiful Makie

For instance: BlueMarbel

I didn’t know the library before, I’ll give it a try. But I want to dominate the library Plots first.

with PlotlyJS.jl you can color a surface according to the values of a scalar field defined on that surface:

using PlotlyJS

n= 256
θ = LinRange(0, 2π, n) #\theta
ϕ = LinRange(-π/2, π/2, n)
Θ = [t for t in θ, p in ϕ] #\Theta
Φ = [p for t in θ, p in ϕ]
x= cos.(Θ) .* cos.(Φ)
y= sin.(Θ) .* cos.(Φ)
z= sin.(Φ)

J = [j for i in 0:n-1, j in 0:n-1]
ScalarF = xor.(J', J)

pl= Plot(surface(x=x, y=y, z=z, colorbar_len=0.7, surfacecolor=ScalarF), 
         Layout(width=500, height=500))

scalarf

The code function directly in Julia. But in jupyter notebook the library PlotlyJS I got the error:

“Unable to load WebIO. Please make sure WebIO works for your Jupyter client. For troubleshooting, please see the WebIO/IJulia documentation.”

In order to plot a colored surface with Plots.jl pyplot(), give a fourth coordinate meshgrid array h to the keyword argument fill_z:

surface(x, y, z; fill_z = h)

Working example with Plots.jl pyplot()

Preparation:

using Plots, StaticArrays, Parameters
default(colorbar=false)

rotate(u, v, w) = let c = cosd, s = sind
    SMatrix{3, 3}(
        c(u)*c(v), c(u)*s(v)*s(w)-s(u)*c(w), c(u)*s(v)*c(w)+s(u)*s(w),
        s(u)*c(v), s(u)*s(v)*s(w)+c(u)*c(w), c(u)*s(v)*s(w)-c(u)*s(w),
            -s(v),      c(v)*s(w),                c(v)*c(w))'
end

"""Assume `surffunc(u, v)` returns `(x, y, z, h)`."""
function plot_surface(u, v, surffunc, param; R = rotate(0, 0, 0), kwargs...)
    xyzh = surffunc.(u', v, Ref(param))
    xyz = (((x, y, z, h),) -> R * SVector(x, y, z)).(xyzh)
    x, y, z = ((a -> a[i]).(xyz) for i in 1:3)
    fill_z = (((x, y, z, h),) -> h).(xyzh)
    surface(x, y, z; fill_z, kwargs...)
end

Colored torus:

pyplot(fmt=:png)

function torusfunc(u, v, param)
    @unpack a, b, d = param
    x = (b + a * cos(u)) * cos(v)
    y = (b + a * cos(u)) * sin(v)
    z =      a * sin(u)
    h = d' * SVector(x, y, z)
    x, y, z, h
end

torusparam = (a = 5, b = 10, d = SVector(1, 0, 1))
n = 50
u_torus = v_torus = range(0, 2π; length = n + 1)
plot_surface(u_torus, v_torus, torusfunc, torusparam; size=(500, 400), 
    lims=(-20, 20), color=:CMRmap, R=rotate(30, 30, 30))

torus

Animation:

pyplot(fmt=:png)
@time anim = @animate for t in range(0, 360; length=41)[1:end-1]
    plot_surface(u_torus, v_torus, torusfunc, torusparam; size=(400, 300),
        lims=(-20, 20), color=:CMRmap, R=rotate(30, 30, 30),
        camera=(t, 20), ticks=false)
end
PyPlot.clf()
gif(anim, "torus.gif")

torus

Colored sphere:

pyplot(fmt=:png)

function spherefunc(u, v, param)
    @unpack r, d, f = param
    x = r * cos(u) * cos(v)
    y = r * cos(u) * sin(v)
    z = r * sin(u)
    h = f(d' * SVector(x, y, z))
    x, y, z, h
end

sphereparam = (
    r = 20,
    d = SVector(0, 0, 1),
    f = h -> (1 + abs(h - 1)) * sin(h)
)
n = 50
u_sphere = range(-π/2, π/2; length = n + 1)
v_sphere = range(0, 2π; length = 2n + 1)
plot_surface(u_sphere, v_sphere, spherefunc, sphereparam; size=(500, 400), 
    lims=(-20, 20), color=:gist_earth, R=rotate(30, 30, 30),
    camera=(30, 20))

sphere

Animation:

pyplot(fmt=:png)
@time anim = @animate for t in range(0, 360; length=41)[1:end-1]
    plot_surface(u_sphere, v_sphere, spherefunc, sphereparam; size=(400, 300),
        lims=(-20, 20), color=:gist_earth, R=rotate(30, 30, 30),
        camera=(t, 20), ticks=false)
end
PyPlot.clf()
gif(anim, "sphere.gif")

sphere

Interactive plotting with plotly()

The plotly() backend can be used to display interactive plots in Jupyter notebook.

1 Like
julia> plot_surface(u_torus, v_torus, torusfunc, torusparam; size=(500, 400),
           lims=(-20, 20), color=:CMRmap, R=rotate(30, 30, 30))
sys:1: MatplotlibDeprecationWarning: Passing the fontdict parameter of _set_ticklabels() positionally is deprecated since Matplotlib 3.3; the parameter will become keyword-only two minor releases later.
sys:1: UserWarning: FixedFormatter should only be used together with FixedLocator
Error showing value of type Plots.Plot{Plots.PyPlotBackend}:
ERROR: MethodError: no method matching getproperty(::Char, ::String)
Closest candidates are:
  getproperty(::PyPlot.ColorMap, ::AbstractString) at C:\Users\Hermesr\.julia\packages\PyPlot\XHEG0\src\colormaps.jl:26
  getproperty(::PyPlot.LazyPyModule, ::AbstractString) at C:\Users\Hermesr\.julia\packages\PyPlot\XHEG0\src\plot3d.jl:16
  getproperty(::PyCall.PyObject, ::AbstractString) at C:\Users\Hermesr\.julia\packages\PyCall\BD546\src\PyCall.jl:311
  ...
Stacktrace:
  [1] py_set_axis_colors(sp::Plots.Subplot{Plots.PyPlotBackend}, ax::PyCall.PyObject, a::Plots.Axis)
    @ Plots C:\Users\Hermesr\.julia\packages\Plots\hyS17\src\backends\pyplot.jl:815
  [2] _before_layout_calcs(plt::Plots.Plot{Plots.PyPlotBackend})
    @ Plots C:\Users\Hermesr\.julia\packages\Plots\hyS17\src\backends\pyplot.jl:1100
  [3] prepare_output(plt::Plots.Plot{Plots.PyPlotBackend})
    @ Plots C:\Users\Hermesr\.julia\packages\Plots\hyS17\src\plot.jl:177
  [4] display(d::VSCodeServer.InlineDisplay, m::MIME{Symbol("image/svg+xml")}, x::Plots.Plot{Plots.PyPlotBackend})
    @ VSCodeServer c:\Users\Hermesr\.vscode\extensions\julialang.language-julia-1.3.30\scripts\packages\VSCodeServer\src\display.jl:0
  [5] display(d::VSCodeServer.InlineDisplay, mime::String, x::Any)
    @ Base.Multimedia .\multimedia.jl:216
  [6] display(d::VSCodeServer.InlineDisplay, x::Plots.Plot{Plots.PyPlotBackend})
    @ VSCodeServer c:\Users\Hermesr\.vscode\extensions\julialang.language-julia-1.3.30\scripts\packages\VSCodeServer\src\display.jl:109
  [7] display(x::Any)
    @ Base.Multimedia .\multimedia.jl:328
  [8] #invokelatest#2
    @ .\essentials.jl:708 [inlined]
  [9] invokelatest
    @ .\essentials.jl:706 [inlined]
 [10] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:247
 [11] (::REPL.var"#40#41"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:231
 [12] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:462
 [13] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:229
 [14] (::REPL.var"#do_respond#61"{Bool, Bool, REPL.var"#72#82"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.LineEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:798
 [15] #invokelatest#2
    @ .\essentials.jl:708 [inlined]
 [16] invokelatest
    @ .\essentials.jl:706 [inlined]
 [17] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
    @ REPL.LineEdit C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\LineEdit.jl:2441
 [18] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
    @ REPL C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\REPL\src\REPL.jl:1126
 [19] (::REPL.var"#44#49"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL .\task.jl:411

My environment is Windows 10 with

using Pkg
println("Julia v", VERSION)
Pkg.status("IJulia")
Pkg.status("PyCall"; mode = PKGMODE_MANIFEST)
Pkg.status("Plots")
Pkg.status("PyPlot")
Pkg.status("Plotly")
Pkg.status("StaticArrays")
Pkg.status("Parameters")

Julia v1.6.2
      Status `D:\.julia\environments\v1.6\Project.toml`
  [7073ff75] IJulia v1.23.2
      Status `D:\.julia\environments\v1.6\Manifest.toml`
  [438e738f] PyCall v1.92.3
      Status `D:\.julia\environments\v1.6\Project.toml`
  [91a5bcdd] Plots v1.20.1
      Status `D:\.julia\environments\v1.6\Project.toml`
  [d330b81b] PyPlot v2.9.0
      Status `D:\.julia\environments\v1.6\Project.toml`
  [58dd65bb] Plotly v0.4.0
      Status `D:\.julia\environments\v1.6\Project.toml`
  [90137ffa] StaticArrays v1.2.12
      Status `D:\.julia\environments\v1.6\Project.toml`
  [d96e819e] Parameters v0.12.2

I use Plots.jl v1.20.1 installed in the directory

D:\.julia\packages\Plots\HcxwM

But your Plots.jl directory is

C:\Users\Hermesr\.julia\packages\Plots\hyS17

Your hyS17 is not equal to my HcxwM.

Probably you are using an older version of Plots.jl due to conflicts with other packages.

First, upgrade installed packages by

pkg> up

and check if Plots.jl is upgraded to version ≥ v1.20.1 or not.

If not, restart Julia in an empty directory and run

pkg> activate .
pkg> add Plots
pkg> status

Confirm the version of Plots.jl is ≥ v1.20.1. Then try to run my code.

1 Like

I have created the Pluto notebook version of the sample Jupyter notebook above.

If you have trouble resolving package dependency issues, you may try the Pluto notebook version. Pluto has a feature automatically resolving package dependencies.

Please copy-and-paste the GitHub URL of Pluto notebook into the browser tab opened by Pluto.

2021-08-19 (3)

The URL that should be pasted is

https://github.com/genkuroki/public/blob/main/0016/Pluto%20-%203d%20colored%20surface%20plots.jl