Davide-sd / sympy-plot-backends

An improved plotting module for SymPy
BSD 3-Clause "New" or "Revised" License
42 stars 9 forks source link

zorder when use_cm=True in 3d graphics #41

Closed imakn634 closed 2 months ago

imakn634 commented 3 months ago

Sorry for a long post. It's nice to plot line_parametric_3d() with use_cm=True:

from sympy.abc import *
from sympy import *
from spb import *

graphics(
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t, 0, 4*pi), 
                     rendering_kw = {'lw':5,'cmap':'autumn','zorder':0},
                     use_cm=True,color_func = sin(t), colorbar = False),
  xlim=(-1,1), ylim=(-1,1), zlim = (0, 1.5), legend = False
);

fig1

(So far I couldn't plot it using Matplotlib plt.plot().)

For educational purpose, I want to draw the spiral motion in the uniform magnetic field, something like this:

graphics(
  # background
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t, 0, pi), 
                     rendering_kw = {'lw':5,'color':'pink','zorder':0},
                     use_cm=False),
  # background
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t,2*pi,3*pi), 
                     rendering_kw = {'lw':5,'color':'pink','zorder':0},
                     use_cm=False), 
  # in the middle
  arrow_3d((0, 0, -1), (0, 0, 3), 
           rendering_kw = {"color":"blue", "lw":10, 'zorder':5}),  
  # foreground
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t, pi, 2*pi), 
                     rendering_kw = {'lw':5,'color':'red','zorder':10},
                     use_cm=False),
  # foreground
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t,3*pi,4*pi), 
                     rendering_kw = {'lw':5,'color':'red','zorder':10},
                     use_cm=False), 

  xlim=(-1,1), ylim=(-1,1), zlim = (0, 1.5), legend = False
);

fig2

The background part of the trajectory is denoted by zorder:0, whereas the foreground part is by zorder:10. OK, it works.

However, when I set use_cm=True, zorder seems not to work properly.

graphics(
  # background
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t, 0, pi), 
                     rendering_kw = {'lw':5,'cmap':'autumn','zorder':0},
                     use_cm=True, color_func = -sin(t), colorbar = False),
  # background? 
  line_parametric_3d(cos(t), sin(t), 0.1*t, (t,2*pi,3*pi), 
                     rendering_kw = {'lw':5,'cmap':'autumn','zorder':0},
                     use_cm=True, color_func = -sin(t), colorbar = False),
  # foreground? 
  arrow_3d((0, 0, -1), (0, 0, 3), 
           rendering_kw = {"color":"blue", "lw":10, 'zorder':5}),  
  xlim=(-1,1), ylim=(-1,1), zlim = (0, 1.5), legend = False
);

fig3

It would be nicer if use_cm=True still respects zorder...

Davide-sd commented 3 months ago

I'm afraid there is nothing I can do. This is a Matplotlib problem, and it is the very reason I developed multiple backends, because Matplotlib sucks at 3D plotting. To be honest, there is no perfect plotting library in the Python ecosystem, each one has its pros and cons, and very few produces good results with 3D plotting.

This plotting module exponses PlotlyBackend and K3DBackend which are usually superior to Matplotlib when it comes to 3D plotting. However, Plotly 3D doesn't have the notion of "arrows", it just shows cones, which makes it hardly suitable to your use case.

I'm going to show you the same example with K3D Jupyter. In order to use it, you either install the full plotting module as mentioned in the installation page, or you just install the necessary requirements:

pip install k3d colorcet

or:

conda install -c conda-forge k3d
conda install -c conda-forge colorcet

I'm going to mention first its limitations, which are particularly important.

import k3d
from sympy import *
from spb import *
var("t")

graphics(
    line_parametric_3d(
        cos(t), sin(t), 0.1*t, (t, 0, 4*pi),
        use_cm=True,color_func = sin(t), colorbar=False,
        rendering_kw={"width": 0.02, "color_map": k3d.matplotlib_color_maps.Autumn}
    ),
    arrow_3d(
        (0, 0, -1), (0, 0, 3),
        rendering_kw={"origin_color": 0x0000ff, "head_color": 0x0000ff, "line_width": 0.02, "head_size": 2}
    ),
    backend=KB
)

image

In the top right of the picture, you see "K3D panel", which can be used to further customize the plot, after it has been created. Particularly useful is the "Objects" tab, where you can see the objects that are inside the plot, and their respective keyword arguments (eventually, to be used in the rendering_kw= parameter).

For objects that requires colormaps, you have a great choice of colormaps. Just execute one by one the following commands to see the output.

dir(k3d.basic_color_maps)
dir(k3d.matplotlib_color_maps)
dir(k3d.paraview_color_maps)

I've see that your parametric curve is scaled on the z-direction. Let's suppose it wasn't:

graphics(
    line_parametric_3d(
        cos(t), sin(t), t, (t, 0, 4*pi),
        use_cm=True,color_func = sin(t), colorbar=False,
        rendering_kw={"width": 0.02, "color_map": k3d.matplotlib_color_maps.Autumn}
    ),
    arrow_3d(
        (0, 0, -1), (0, 0, 15),
        rendering_kw={"origin_color": 0x0000ff, "head_color": 0x0000ff, "line_width": 0.02, "head_size": 2}
    ),
    backend=KB
)

image

Here you can see the limitations of the equal aspect ratio on all axis: the visualization is stretched too much on the z-axis, which also causes the grids to be weirdly stretched. In this case I usually set the transformation keyword arguments in order to make the plot more readable:

z_transform = lambda z: z/10
graphics(
    line_parametric_3d(
        cos(t), sin(t), t, (t, 0, 4*pi),
        use_cm=True,color_func = sin(t), colorbar=False,
        rendering_kw={"width": 0.02, "color_map": k3d.matplotlib_color_maps.Autumn},
        tz=z_transform
    ),
    arrow_3d(
        (0, 0, -1), (0, 0, 15),
        rendering_kw={"origin_color": 0x0000ff, "head_color": 0x0000ff, "line_width": 0.02, "head_size": 2},
        tz=z_transform
    ),
    backend=KB, grid=False
)

image

imakn634 commented 3 months ago

Thank you very much for the detailed explanation. I now understand this is a Matplotlib problem. K3D looks interesting. I will try it. Thanks again!