Closed
Description
Bug report
Bug summary
- When plotting a parametrized line in 3D using
LineCollection
, aValueError
is thrown whenAxes3D.add_collection_3d
upconverts 2D Collections with thezs
parameter.
In the MWE below, this is because this line in_paths_to_3d_segments
tries to broadcast a 2D array (number of line segments on the first axis and segment start & end on the second) to a 1D array (number of line segments):
matplotlib/lib/mpl_toolkits/mplot3d/art3d.py
Line 234 in 137edd5
Commenting out this line will make the example work. - Additionally,
add_collection3d
does not return thecollection
likeadd_collection
does (it does not return anything). Hence, you cannot pass thecollection
object to, e.g., a colorbar call:
matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py
Lines 2180 to 2186 in 137edd5
Simply returning the return value of the call tosuper().add_collection
does the trick for me here:
collection = super().add_collection(col)
return collection
Code for reproduction
A minimal working example (including the reason why I use LineCollection
instead of plot
, namely a multicolored line) stitched together from two official examples (1, 2) is:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
fig = plt.figure()
ax = fig.gca(projection='3d')
norm = plt.Normalize(0, 2*np.pi)
# 2D LineCollection from x & y values
lc = LineCollection(segments[:, :, :2], cmap='twilight', norm=norm)
lc.set_array(np.mod(theta, 2*np.pi))
# Add 2D collection at z values to ax
line = ax.add_collection3d(lc, zs=segments[:, :, 2]) # line is None
# fig.colorbar(line, ax=ax) # Would fail because add_collection3d does not return the collection
ax.set_xlim(-5, 5)
ax.set_ylim(-4, 6)
ax.set_zlim(-2, 2)
plt.show()
Actual outcome
Traceback (most recent call last):
File "<ipython-input-1-d8492282873b>", line 23, in <module>
line = ax.add_collection3d(lc, zs=segments[:, :, 2])
File "c:\users\hangleiter\appdata\local\continuum\anaconda3\envs\py38\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 2178, in add_collection3d
art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir)
File "c:\users\hangleiter\appdata\local\continuum\anaconda3\envs\py38\lib\site-packages\mpl_toolkits\mplot3d\art3d.py", line 332, in line_collection_2d_to_3d
segments3d = _paths_to_3d_segments(col.get_paths(), zs, zdir)
File "c:\users\hangleiter\appdata\local\continuum\anaconda3\envs\py38\lib\site-packages\mpl_toolkits\mplot3d\art3d.py", line 234, in _paths_to_3d_segments
zs = np.broadcast_to(zs, len(paths))
File "<__array_function__ internals>", line 5, in broadcast_to
File "C:\Users\hangleiter\AppData\Local\Continuum\anaconda3\envs\py38\lib\site-packages\numpy\lib\stride_tricks.py", line 182, in broadcast_to
return _broadcast_to(array, shape, subok=subok, readonly=True)
File "C:\Users\hangleiter\AppData\Local\Continuum\anaconda3\envs\py38\lib\site-packages\numpy\lib\stride_tricks.py", line 125, in _broadcast_to
it = np.nditer(
ValueError: input operand has more dimensions than allowed by the axis remapping
Expected outcome
This is the result if both of the above fixes are applied:
Matplotlib version
- Operating system:
Windows
- Matplotlib version:
3.2.2
- Matplotlib backend (
print(matplotlib.get_backend())
):module://ipykernel.pylab.backend_inline
- Python version:
3.8.3
- Numpy version:
1.18.5
Matplotlib is installed via conda:defaults
.