Matplotlib line width based on axis, not on points

Question:

If you set a line width in Matplotlib, you have to give the line width in points. In my case, I have two circles, both with radius R and I want to connect them with a line. I want this line to be 2*R wide in order to get a rod-shape. But when I say myLines[i].set_linewidth(2*R) this makes the lines always a specific thickness, regardless of how much I have zoomed in.

Is there a way to make lines a specific thickness not based on the number of pixels or points, but scaling with the axis? How can I make my line have the same width as the diameter of my circles?

I hope I explained myself well enough and I am looking forward to an answer.

Asked By: R van Genderen

||

Answers:

As you already figured out, linewidths are specified in axis space, not data space. To draw a line in data space, draw a rectangle instead:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Circle

r = 5 # rod radius
x1, y1 = (0,0) # left end of rod
x2, y2 = (10,0) # right end of rod

# create 2 circles and a joining rectangle
c1 = Circle((x1, y1), r, color='r')
c2 = Circle((x2, y2), r)
rect = Rectangle((x1, y1-r), width=x2-x1, height=2*r)

# plot artists
fig, ax = plt.subplots(1,1)
for artist in [c2, rect, c1]:
    ax.add_artist(artist)

# need to set axis limits manually
ax.set_xlim(x1-r-1, x2+r+1)
ax.set_ylim(y1-r-1, y2+r+1)

# set aspect so circle don't become oval
ax.set_aspect('equal')

plt.show()

enter image description here

Answered By: Paul Brodersen

Line in Data units

In order to draw a line with the linewidth in data units, you may want to have a look at this answer.

It uses a class data_linewidth_plot which closely resembles the plt.plot() command’s signature.

l = data_linewidth_plot( x, y, ax=ax, label='some line', linewidth = 1, alpha = 0.4)

The linewidth argument is interpreted in (y-)data units.

Using this solution there is not even any need for drawing circles, since one may simply use the solid_capstyle="round" argument.

R=0.5
l = data_linewidth_plot( [0,3], [0.7,1.4], ax=ax, solid_capstyle="round", 
                        linewidth = 2*R, alpha = 0.4)

enter image description here

Rod shape

A rod is much more easily produced using a rectange and two circles.
enter image description here

What about this:

lw_in_axis_units = 10  # whatever value you want in axis units

# get size of axis in display units
ax_width, ax_height = ax.get_position().size

# calculate normalized line width
norm_linewidth = lw_in_axis_units / max(ax_width, ax_height)

# current DPI
dpi = plt.gcf().dpi

lw_in_points = norm_linewidth * dpi

plt.plot(X, Y, lw=lw_in_points)
Answered By: Tony Power
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.