11

Based on the matplotlib example code I constructed a 3D version of a multicolored line. I am working in a jupyter notebook and by using %matplotlib notebook I may zoom into the plot and the corner edges are rendered smoothly in my browser - perfect! However, when I export the plot as png or pdf file for further usage the corner edges are "jagged".

Any ideas how to smoothen the 3D-multicolored line?

edges 3D png-file

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap, BoundaryNorm    
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Line3DCollection
%matplotlib notebook

# Generate random data
np.random.seed(1)
n = 20 # number of data points
#set x,y,z data
x = np.random.uniform(0, 1, n)
y  = np.random.uniform(0, 1, n)
z = np.arange(0,n)

# Create a colormap for red, green and blue and a norm to color
# f' < -0.5 red, f' > 0.5 blue, and the rest green
cmap = ListedColormap(['r', 'g', 'b'])
norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)

#################
### 3D Figure ###
#################

# Create a set of line segments
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

# Create the 3D-line collection object
lc = Line3DCollection(segments, cmap=plt.get_cmap('copper'),
                    norm=plt.Normalize(0, n))
lc.set_array(z) 
lc.set_linewidth(2)

#plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_zlim(0, max(z))
plt.title('3D-Figure')
ax.add_collection3d(lc, zs=z, zdir='z')

#save plot
plt.savefig('3D_Line.png', dpi=600, facecolor='w', edgecolor='w',
            orientation='portrait')
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
eotp
  • 312
  • 3
  • 13
  • What version of matplotlib are you using? It worked fine for me on matplotlib 2.0 – Seanny123 Feb 15 '17 at 07:51
  • @Seanny123 Thanks for the comment. Well, back then I did not use matplotlib 2.0. I just updated to matplotlib 2.0, however, still the saved figure appears "jagged". I am working in a jupyter notebook and by using `%matplotlib notebook` I may zoom into the plot and the corner edges are rendered smoothly in my browser - perfect! -; however, when I export the plot as _png_ or _pdf_ files for further usage the corner edges are still "jagged". Thus, no improvement. – eotp Feb 16 '17 at 10:16
  • You might want to add that information by editing your question. Also, maybe removing the `ipython` specific code and the 2D plot code to make your post more understandable. Finally, it would also help if you posted the image inline, instead of linking to it. – Seanny123 Feb 28 '17 at 08:15
  • @Seanny123 I simplified the question. Thanks for the comments. – eotp Mar 01 '17 at 13:00
  • I've got the same problem with 2D `LineCollection`. The edges of the lines are partly overlapping and look jagged. Did you find a solution to this problem? – JE_Muc Dec 04 '17 at 10:08
  • @Scotty1 No sorry, I still have no idea how to solve this issue. – eotp Dec 05 '17 at 16:03
  • Ok. I tried to solve it by interpolating the vertices. It works, but I the effort is too big to implement it in my code... – JE_Muc Dec 08 '17 at 11:54

2 Answers2

4

I think join style is what controls the look of segment joints. Line3DCollection does have a set_joinstyle() function, but that doesn't seem to make any difference. So I've to abandon Line3DCollection and plot the line segment by segment, and for each segment, call its set_solid_capstyle('round').

Below is what works for me:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Generate random data
np.random.seed(1)
n = 20 # number of data points
#set x,y,z data
x = np.random.uniform(0, 1, n)
y  = np.random.uniform(0, 1, n)
z = np.arange(0,n)


#################
### 3D Figure ###
#################

# Create a set of line segments
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

cmap=plt.get_cmap('copper')
colors=[cmap(float(ii)/(n-1)) for ii in range(n-1)]

#plot
fig = plt.figure()
ax = fig.gca(projection='3d')

for ii in range(n-1):
    segii=segments[ii]
    lii,=ax.plot(segii[:,0],segii[:,1],segii[:,2],color=colors[ii],linewidth=2)
    #lii.set_dash_joinstyle('round')
    #lii.set_solid_joinstyle('round')
    lii.set_solid_capstyle('round')

ax.set_zlim(0, max(z))
plt.title('3D-Figure')

#save plot
plt.savefig('3D_Line.png', dpi=600, facecolor='w', edgecolor='w',
            orientation='portrait')

Output image at zoom:

enter image description here

Jason
  • 2,950
  • 2
  • 30
  • 50
0

Building on @Jason's answer, I actually used the capstyle directly in the LineCollection init rather than joinstyle which indeed did nothing.

A snippet from my code:

lc = LineCollection(segments, cmap=viridis, norm=norm, joinstyle="round", capstyle="round")

Of course it's not perfect, but it somehow reduces the issue.

Zaccharie Ramzi
  • 2,106
  • 1
  • 18
  • 37