Reputation: 51
I'm attempting to subclass matplotlib.lines.Line2D
to create lines whose width and dash spacings are in the same coordinates as the data being plotted. I've expanded on this answer to try to support dashed lines (with the dash spacings also being in data units) and have the following code currently:
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
class LineDataUnits(Line2D):
"""
A Line2D object, but with the linewidth and dash properties defined in data coordinates.
"""
def __init__(self, *args, **kwargs):
_lw_data = kwargs.pop("linewidth", 1)
_dashes_data = kwargs.pop("dashes", (1,))
super().__init__(*args, **kwargs)
self._lw_data = _lw_data
self._dashes_data = _dashes_data
self._dashOffset = 0
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
def _get_dashes(self):
if self.axes is not None:
ppd = 72./self.axes.figure.dpi
trans = self.axes.transData.transform
return tuple([((trans((1, dash_data))-trans((0, 0)))*ppd)[1] for dash_data in self._dashes_data])
else:
return tuple((1, 0))
def _set_dashes(self, dashes):
self._dashes_data = dashes
_linewidth = property(_get_lw, _set_lw)
_dashSeq = property(_get_dashes, _set_dashes)
if __name__ == "__main__":
fig, ax = plt.subplots()
line1 = Line2D([0, 10], [3, 3], linewidth=10, color="blue", dashes=(3, 3), dash_capstyle="round")
line2 = LineDataUnits([0, 10], [6, 6], linewidth=0.5, color="red", dashes=(1, 1), dash_capstyle="round")
print("dash_capstyle:", line2.get_dash_capstyle())
ax.add_line(line1)
ax.add_line(line2)
ax.set_xticks(range(10))
ax.grid()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
plt.show()
Which yields the following graph:
When I zoom in and out, the dash spacings do seem to remain constant in the data units, however as you can see in the graph, their values are not calculated correctly. Furthermore, it appears setting the dash_capstyle
parameter in this class seems to have no effect.
Any help would be greatly appreciated.
Upvotes: 3
Views: 259
Reputation: 51
After much more experimentation, I managed to get it working properly. Firstly, even though the line was being plotted with dashes, the Line2D
still had its _linestyle
parameter set to "-" (solid), which caused the cap style argument to be ignored. Secondly, there was an error in my conversion from data units to points. With the changes below, it now works perfectly.
# in __init__()
super().__init__(*args, **kwargs):
+ self.set_linestyle("--")
self._lw_data = _lw_data
# in _get_dashes()
- return tuple([((trans((1, dash_data))-trans((0, 0)))*ppd)[1] for dash_data in self._dashes_data])
+ dpu = (trans((1, 1)) - trans((0, 0)))[0]
+ return tuple([u*dpu*ppd for u in self._dashes_data])
Upvotes: 2