axisline_style.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. """
  2. Provides classes to style the axis lines.
  3. """
  4. import math
  5. import numpy as np
  6. import matplotlib as mpl
  7. from matplotlib.patches import _Style, FancyArrowPatch
  8. from matplotlib.path import Path
  9. from matplotlib.transforms import IdentityTransform
  10. class _FancyAxislineStyle:
  11. class SimpleArrow(FancyArrowPatch):
  12. """The artist class that will be returned for SimpleArrow style."""
  13. _ARROW_STYLE = "->"
  14. def __init__(self, axis_artist, line_path, transform,
  15. line_mutation_scale):
  16. self._axis_artist = axis_artist
  17. self._line_transform = transform
  18. self._line_path = line_path
  19. self._line_mutation_scale = line_mutation_scale
  20. FancyArrowPatch.__init__(self,
  21. path=self._line_path,
  22. arrowstyle=self._ARROW_STYLE,
  23. patchA=None,
  24. patchB=None,
  25. shrinkA=0.,
  26. shrinkB=0.,
  27. mutation_scale=line_mutation_scale,
  28. mutation_aspect=None,
  29. transform=IdentityTransform(),
  30. )
  31. def set_line_mutation_scale(self, scale):
  32. self.set_mutation_scale(scale*self._line_mutation_scale)
  33. def _extend_path(self, path, mutation_size=10):
  34. """
  35. Extend the path to make a room for drawing arrow.
  36. """
  37. (x0, y0), (x1, y1) = path.vertices[-2:]
  38. theta = math.atan2(y1 - y0, x1 - x0)
  39. x2 = x1 + math.cos(theta) * mutation_size
  40. y2 = y1 + math.sin(theta) * mutation_size
  41. if path.codes is None:
  42. return Path(np.concatenate([path.vertices, [[x2, y2]]]))
  43. else:
  44. return Path(np.concatenate([path.vertices, [[x2, y2]]]),
  45. np.concatenate([path.codes, [Path.LINETO]]))
  46. def set_path(self, path):
  47. self._line_path = path
  48. def draw(self, renderer):
  49. """
  50. Draw the axis line.
  51. 1) Transform the path to the display coordinate.
  52. 2) Extend the path to make a room for arrow.
  53. 3) Update the path of the FancyArrowPatch.
  54. 4) Draw.
  55. """
  56. path_in_disp = self._line_transform.transform_path(self._line_path)
  57. mutation_size = self.get_mutation_scale() # line_mutation_scale()
  58. extended_path = self._extend_path(path_in_disp,
  59. mutation_size=mutation_size)
  60. self._path_original = extended_path
  61. FancyArrowPatch.draw(self, renderer)
  62. def get_window_extent(self, renderer=None):
  63. path_in_disp = self._line_transform.transform_path(self._line_path)
  64. mutation_size = self.get_mutation_scale() # line_mutation_scale()
  65. extended_path = self._extend_path(path_in_disp,
  66. mutation_size=mutation_size)
  67. self._path_original = extended_path
  68. return FancyArrowPatch.get_window_extent(self, renderer)
  69. class FilledArrow(SimpleArrow):
  70. """The artist class that will be returned for FilledArrow style."""
  71. _ARROW_STYLE = "-|>"
  72. def __init__(self, axis_artist, line_path, transform,
  73. line_mutation_scale, facecolor):
  74. super().__init__(axis_artist, line_path, transform,
  75. line_mutation_scale)
  76. self.set_facecolor(facecolor)
  77. class AxislineStyle(_Style):
  78. """
  79. A container class which defines style classes for AxisArtists.
  80. An instance of any axisline style class is a callable object,
  81. whose call signature is ::
  82. __call__(self, axis_artist, path, transform)
  83. When called, this should return an `.Artist` with the following methods::
  84. def set_path(self, path):
  85. # set the path for axisline.
  86. def set_line_mutation_scale(self, scale):
  87. # set the scale
  88. def draw(self, renderer):
  89. # draw
  90. """
  91. _style_list = {}
  92. class _Base:
  93. # The derived classes are required to be able to be initialized
  94. # w/o arguments, i.e., all its argument (except self) must have
  95. # the default values.
  96. def __init__(self):
  97. """
  98. initialization.
  99. """
  100. super().__init__()
  101. def __call__(self, axis_artist, transform):
  102. """
  103. Given the AxisArtist instance, and transform for the path (set_path
  104. method), return the Matplotlib artist for drawing the axis line.
  105. """
  106. return self.new_line(axis_artist, transform)
  107. class SimpleArrow(_Base):
  108. """
  109. A simple arrow.
  110. """
  111. ArrowAxisClass = _FancyAxislineStyle.SimpleArrow
  112. def __init__(self, size=1):
  113. """
  114. Parameters
  115. ----------
  116. size : float
  117. Size of the arrow as a fraction of the ticklabel size.
  118. """
  119. self.size = size
  120. super().__init__()
  121. def new_line(self, axis_artist, transform):
  122. linepath = Path([(0, 0), (0, 1)])
  123. axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
  124. line_mutation_scale=self.size)
  125. return axisline
  126. _style_list["->"] = SimpleArrow
  127. class FilledArrow(SimpleArrow):
  128. """
  129. An arrow with a filled head.
  130. """
  131. ArrowAxisClass = _FancyAxislineStyle.FilledArrow
  132. def __init__(self, size=1, facecolor=None):
  133. """
  134. Parameters
  135. ----------
  136. size : float
  137. Size of the arrow as a fraction of the ticklabel size.
  138. facecolor : color, default: :rc:`axes.edgecolor`
  139. Fill color.
  140. .. versionadded:: 3.7
  141. """
  142. if facecolor is None:
  143. facecolor = mpl.rcParams['axes.edgecolor']
  144. self.size = size
  145. self._facecolor = facecolor
  146. super().__init__(size=size)
  147. def new_line(self, axis_artist, transform):
  148. linepath = Path([(0, 0), (0, 1)])
  149. axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
  150. line_mutation_scale=self.size,
  151. facecolor=self._facecolor)
  152. return axisline
  153. _style_list["-|>"] = FilledArrow