123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- """
- Provides classes to style the axis lines.
- """
- import math
- import numpy as np
- import matplotlib as mpl
- from matplotlib.patches import _Style, FancyArrowPatch
- from matplotlib.path import Path
- from matplotlib.transforms import IdentityTransform
- class _FancyAxislineStyle:
- class SimpleArrow(FancyArrowPatch):
- """The artist class that will be returned for SimpleArrow style."""
- _ARROW_STYLE = "->"
- def __init__(self, axis_artist, line_path, transform,
- line_mutation_scale):
- self._axis_artist = axis_artist
- self._line_transform = transform
- self._line_path = line_path
- self._line_mutation_scale = line_mutation_scale
- FancyArrowPatch.__init__(self,
- path=self._line_path,
- arrowstyle=self._ARROW_STYLE,
- patchA=None,
- patchB=None,
- shrinkA=0.,
- shrinkB=0.,
- mutation_scale=line_mutation_scale,
- mutation_aspect=None,
- transform=IdentityTransform(),
- )
- def set_line_mutation_scale(self, scale):
- self.set_mutation_scale(scale*self._line_mutation_scale)
- def _extend_path(self, path, mutation_size=10):
- """
- Extend the path to make a room for drawing arrow.
- """
- (x0, y0), (x1, y1) = path.vertices[-2:]
- theta = math.atan2(y1 - y0, x1 - x0)
- x2 = x1 + math.cos(theta) * mutation_size
- y2 = y1 + math.sin(theta) * mutation_size
- if path.codes is None:
- return Path(np.concatenate([path.vertices, [[x2, y2]]]))
- else:
- return Path(np.concatenate([path.vertices, [[x2, y2]]]),
- np.concatenate([path.codes, [Path.LINETO]]))
- def set_path(self, path):
- self._line_path = path
- def draw(self, renderer):
- """
- Draw the axis line.
- 1) Transform the path to the display coordinate.
- 2) Extend the path to make a room for arrow.
- 3) Update the path of the FancyArrowPatch.
- 4) Draw.
- """
- path_in_disp = self._line_transform.transform_path(self._line_path)
- mutation_size = self.get_mutation_scale() # line_mutation_scale()
- extended_path = self._extend_path(path_in_disp,
- mutation_size=mutation_size)
- self._path_original = extended_path
- FancyArrowPatch.draw(self, renderer)
- def get_window_extent(self, renderer=None):
- path_in_disp = self._line_transform.transform_path(self._line_path)
- mutation_size = self.get_mutation_scale() # line_mutation_scale()
- extended_path = self._extend_path(path_in_disp,
- mutation_size=mutation_size)
- self._path_original = extended_path
- return FancyArrowPatch.get_window_extent(self, renderer)
- class FilledArrow(SimpleArrow):
- """The artist class that will be returned for FilledArrow style."""
- _ARROW_STYLE = "-|>"
- def __init__(self, axis_artist, line_path, transform,
- line_mutation_scale, facecolor):
- super().__init__(axis_artist, line_path, transform,
- line_mutation_scale)
- self.set_facecolor(facecolor)
- class AxislineStyle(_Style):
- """
- A container class which defines style classes for AxisArtists.
- An instance of any axisline style class is a callable object,
- whose call signature is ::
- __call__(self, axis_artist, path, transform)
- When called, this should return an `.Artist` with the following methods::
- def set_path(self, path):
- # set the path for axisline.
- def set_line_mutation_scale(self, scale):
- # set the scale
- def draw(self, renderer):
- # draw
- """
- _style_list = {}
- class _Base:
- # The derived classes are required to be able to be initialized
- # w/o arguments, i.e., all its argument (except self) must have
- # the default values.
- def __init__(self):
- """
- initialization.
- """
- super().__init__()
- def __call__(self, axis_artist, transform):
- """
- Given the AxisArtist instance, and transform for the path (set_path
- method), return the Matplotlib artist for drawing the axis line.
- """
- return self.new_line(axis_artist, transform)
- class SimpleArrow(_Base):
- """
- A simple arrow.
- """
- ArrowAxisClass = _FancyAxislineStyle.SimpleArrow
- def __init__(self, size=1):
- """
- Parameters
- ----------
- size : float
- Size of the arrow as a fraction of the ticklabel size.
- """
- self.size = size
- super().__init__()
- def new_line(self, axis_artist, transform):
- linepath = Path([(0, 0), (0, 1)])
- axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
- line_mutation_scale=self.size)
- return axisline
- _style_list["->"] = SimpleArrow
- class FilledArrow(SimpleArrow):
- """
- An arrow with a filled head.
- """
- ArrowAxisClass = _FancyAxislineStyle.FilledArrow
- def __init__(self, size=1, facecolor=None):
- """
- Parameters
- ----------
- size : float
- Size of the arrow as a fraction of the ticklabel size.
- facecolor : color, default: :rc:`axes.edgecolor`
- Fill color.
- .. versionadded:: 3.7
- """
- if facecolor is None:
- facecolor = mpl.rcParams['axes.edgecolor']
- self.size = size
- self._facecolor = facecolor
- super().__init__(size=size)
- def new_line(self, axis_artist, transform):
- linepath = Path([(0, 0), (0, 1)])
- axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
- line_mutation_scale=self.size,
- facecolor=self._facecolor)
- return axisline
- _style_list["-|>"] = FilledArrow
|