I would like to create a matplotlib.patches.PathPatch
like patch, with the linewidth given in data units (not points).
I am aware that for patches with a regular shape, one can mimic such an object by drawing two patches of different sizes on top of each other.
However, this approach becomes a lot more computationally involved for arbitrary shapes (need to compute a parallel line for an arbitrary shape).
Furthermore, I would like to retain the PathPath
object methods, so ultimately I am looking for a class derived from PathPatch
or Patch
.
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch, Rectangle
fig, (ax1, ax2) = plt.subplots(1, 2, sharex=True, sharey=True)
origin = (0, 0)
width = 1
height = 2
lw = 0.25
outer = Rectangle((origin[0], origin[1]), width, height, facecolor='darkblue', zorder=1)
inner = Rectangle((origin[0]+lw, origin[1]+lw), width-2*lw, height-2*lw, facecolor='lightblue', zorder=2)
ax1.add_patch(outer)
ax1.add_patch(inner)
ax1.axis([-0.5, 1.5, -0.5, 2.5])
ax1.set_title('Desired')
path = outer.get_path().transformed(outer.get_patch_transform())
pathpatch = PathPatch(path, facecolor='lightblue', edgecolor='darkblue', linewidth=lw)
ax2.add_patch(pathpatch)
ax2.set_title('Actual')
plt.show()
There is a different SO post that derives from Line2D
to create a line with widths in data units.
class LineDataUnits(Line2D):
# https://stackoverflow.com/a/42972469/2912349
def __init__(self, *args, **kwargs):
_lw_data = kwargs.pop("linewidth", 1)
super().__init__(*args, **kwargs)
self._lw_data = _lw_data
def _get_lw(self):
if self.axes is not None:
ppd = 72./self.axes.figure.dpi
trans = self.axes.transData.transform
return ((trans((1, self._lw_data))-trans((0, 0)))*ppd)[1]
else:
return 1
def _set_lw(self, lw):
self._lw_data = lw
_linewidth = property(_get_lw, _set_lw)
I don't quite understand how this implementation works (for example, no methods of Line2D
seem to be overwritten)
Nevertheless, I have tried copying this approach (class LineDataUnits(Line2D)
-> class PathPatchDataUnits(PathPatch)
), but -- unsurprisingly -- cannot get it to work (AttributeError: 'NoneType' object has no attribute 'set_figure'
).