zorder when use_cm=True in 3d graphics
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
);
(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
);
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
);
It would be nicer if use_cm=True still respects zorder...
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.
- The code needs to be executed inside Jupyter Lab or Jupyter Notebook.
- it's not possible to set the size of the plot. It just adapts to the width of the notebook.
- It is Javascript-based: it produces excellent results. However, if you are working on an very old machine it might be difficult to handle them, especially if you use the full-screen option.
- It has an equal aspect ratio on all axis, which cannot be changed (that's why I implemented the transformation keywords,
tx=, ty=, tz=(more on this later). - It's grid system is very limited.
- Only show one colorbar, and it can't be hidden.
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
)
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
)
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:
- these keyword arguments allows a clearer separation of intents. I can plot the unmodified symbolic expressions, while making it clear that I'm scaling a particular axis.
- when using these keyword arguments, it might be useful turn off the grids because they may no longer represent the symbolic expression.
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
)
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!