def _draw_grid(self, renderer):

        renderer.open_group('grid lines')

        self._update_ticks()

        if self.grid_lines_kwargs['visible']:

            if self._grid_type == 'lines':
                self._update_grid_lines()
            else:
                self._update_grid_contour()

            if self._grid_type == 'lines':

                frame_patch = self.frame.patch
                for path in self.grid_lines:
                    p = PathPatch(path, **self.grid_lines_kwargs)
                    p.set_clip_path(frame_patch)
                    p.draw(renderer)

            elif self._grid is not None:

                for line in self._grid.collections:
                    line.set(**self.grid_lines_kwargs)
                    line.draw(renderer)

        renderer.close_group('grid lines')
Exemple #2
0
    def _draw(self, renderer, bboxes, ticklabels_bbox):

        renderer.open_group('coordinate_axis')

        self._update_ticks(renderer)

        self.ticks.draw(renderer)
        self.ticklabels.draw(renderer,
                             bboxes=bboxes,
                             ticklabels_bbox=ticklabels_bbox)

        if self.grid_lines_kwargs['visible']:

            if self._grid_type == 'lines':
                self._update_grid_lines()
            else:
                self._update_grid_contour()

            if self._grid_type == 'lines':

                frame_patch = self.frame.patch
                for path in self.grid_lines:
                    p = PathPatch(path, **self.grid_lines_kwargs)
                    p.set_clip_path(frame_patch)
                    p.draw(renderer)

            else:

                for line in self._grid.collections:
                    line.set(**self.grid_lines_kwargs)
                    line.draw(renderer)

        renderer.close_group('coordinate_axis')
Exemple #3
0
    def _draw(self, renderer, bboxes, ticklabels_bbox):

        renderer.open_group('coordinate_axis')

        self._update_ticks(renderer)

        self.ticks.draw(renderer)
        self.ticklabels.draw(renderer, bboxes=bboxes,
                             ticklabels_bbox=ticklabels_bbox)

        if self.grid_lines_kwargs['visible']:

            if self._grid_type == 'lines':
                self._update_grid_lines()
            else:
                self._update_grid_contour()

            if self._grid_type == 'lines':

                frame_patch = self.frame.patch
                for path in self.grid_lines:
                    p = PathPatch(path, **self.grid_lines_kwargs)
                    p.set_clip_path(frame_patch)
                    p.draw(renderer)

            elif self._grid is not None:

                for line in self._grid.collections:
                    line.set(**self.grid_lines_kwargs)
                    line.draw(renderer)

        renderer.close_group('coordinate_axis')
    def _draw(self, renderer, bboxes):

        renderer.open_group('coordinate_axis')

        self._update_ticks(renderer)
        self._update_grid()
        self.ticks.draw(renderer)
        self.ticklabels.draw(renderer, bboxes=bboxes)

        if self.grid_lines_kwargs['visible']:
            for path in self.grid_lines:
                p = PathPatch(path, **self.grid_lines_kwargs)
                p.set_clip_path(self.frame.path, Affine2D())
                p.draw(renderer)

        renderer.close_group('coordinate_axis')
Exemple #5
0
class AxisArtist(martist.Artist):
    """
    An artist which draws axis (a line along which the n-th axes coord
    is constant) line, ticks, ticklabels, and axis label.
    """

    zorder = 2.5

    @_api.deprecated("3.4")
    @_api.classproperty
    def ZORDER(cls):
        return cls.zorder

    @property
    def LABELPAD(self):
        return self.label.get_pad()

    @LABELPAD.setter
    def LABELPAD(self, v):
        self.label.set_pad(v)

    def __init__(self,
                 axes,
                 helper,
                 offset=None,
                 axis_direction="bottom",
                 **kwargs):
        """
        Parameters
        ----------
        axes : `mpl_toolkits.axisartist.axislines.Axes`
        helper : `~mpl_toolkits.axisartist.axislines.AxisArtistHelper`
        """
        #axes is also used to follow the axis attribute (tick color, etc).

        super().__init__(**kwargs)

        self.axes = axes

        self._axis_artist_helper = helper

        if offset is None:
            offset = (0, 0)
        self.offset_transform = ScaledTranslation(
            *offset,
            Affine2D().scale(1 / 72)  # points to inches.
            + self.axes.figure.dpi_scale_trans)

        if axis_direction in ["left", "right"]:
            self.axis = axes.yaxis
        else:
            self.axis = axes.xaxis

        self._axisline_style = None
        self._axis_direction = axis_direction

        self._init_line()
        self._init_ticks(**kwargs)
        self._init_offsetText(axis_direction)
        self._init_label()

        # axis direction
        self._ticklabel_add_angle = 0.
        self._axislabel_add_angle = 0.
        self.set_axis_direction(axis_direction)

    @_api.deprecated("3.3")
    @property
    def dpi_transform(self):
        return Affine2D().scale(1 / 72) + self.axes.figure.dpi_scale_trans

    # axis direction

    def set_axis_direction(self, axis_direction):
        """
        Adjust the direction, text angle, text alignment of
        ticklabels, labels following the matplotlib convention for
        the rectangle axes.

        The *axis_direction* must be one of [left, right, bottom, top].

        =====================    ========== ========= ========== ==========
        property                 left       bottom    right      top
        =====================    ========== ========= ========== ==========
        ticklabels location      "-"        "+"       "+"        "-"
        axislabel location       "-"        "+"       "+"        "-"
        ticklabels angle         90         0         -90        180
        ticklabel va             center     baseline  center     baseline
        ticklabel ha             right      center    right      center
        axislabel angle          180        0         0          180
        axislabel va             center     top       center     bottom
        axislabel ha             right      center    right      center
        =====================    ========== ========= ========== ==========

        Note that the direction "+" and "-" are relative to the direction of
        the increasing coordinate. Also, the text angles are actually
        relative to (90 + angle of the direction to the ticklabel),
        which gives 0 for bottom axis.
        """
        self.major_ticklabels.set_axis_direction(axis_direction)
        self.label.set_axis_direction(axis_direction)
        self._axis_direction = axis_direction
        if axis_direction in ["left", "top"]:
            self.set_ticklabel_direction("-")
            self.set_axislabel_direction("-")
        else:
            self.set_ticklabel_direction("+")
            self.set_axislabel_direction("+")

    def set_ticklabel_direction(self, tick_direction):
        r"""
        Adjust the direction of the ticklabel.

        Note that the *label_direction*\s '+' and '-' are relative to the
        direction of the increasing coordinate.

        Parameters
        ----------
        tick_direction : {"+", "-"}
        """
        self._ticklabel_add_angle = _api.check_getitem(
            {
                "+": 0,
                "-": 180
            }, tick_direction=tick_direction)

    def invert_ticklabel_direction(self):
        self._ticklabel_add_angle = (self._ticklabel_add_angle + 180) % 360
        self.major_ticklabels.invert_axis_direction()
        self.minor_ticklabels.invert_axis_direction()

    def set_axislabel_direction(self, label_direction):
        r"""
        Adjust the direction of the axislabel.

        Note that the *label_direction*\s '+' and '-' are relative to the
        direction of the increasing coordinate.

        Parameters
        ----------
        label_direction : {"+", "-"}
        """
        self._axislabel_add_angle = _api.check_getitem(
            {
                "+": 0,
                "-": 180
            }, label_direction=label_direction)

    def get_transform(self):
        return self.axes.transAxes + self.offset_transform

    def get_helper(self):
        """
        Return axis artist helper instance.
        """
        return self._axis_artist_helper

    def set_axisline_style(self, axisline_style=None, **kwargs):
        """
        Set the axisline style.

        The new style is completely defined by the passed attributes. Existing
        style attributes are forgotten.

        Parameters
        ----------
        axisline_style : str or None
            The line style, e.g. '->', optionally followed by a comma-separated
            list of attributes. Alternatively, the attributes can be provided
            as keywords.

            If *None* this returns a string containing the available styles.

        Examples
        --------
        The following two commands are equal:
        >>> set_axisline_style("->,size=1.5")
        >>> set_axisline_style("->", size=1.5)
        """
        if axisline_style is None:
            return AxislineStyle.pprint_styles()

        if isinstance(axisline_style, AxislineStyle._Base):
            self._axisline_style = axisline_style
        else:
            self._axisline_style = AxislineStyle(axisline_style, **kwargs)

        self._init_line()

    def get_axisline_style(self):
        """Return the current axisline style."""
        return self._axisline_style

    def _init_line(self):
        """
        Initialize the *line* artist that is responsible to draw the axis line.
        """
        tran = (self._axis_artist_helper.get_line_transform(self.axes) +
                self.offset_transform)

        axisline_style = self.get_axisline_style()
        if axisline_style is None:
            self.line = PathPatch(self._axis_artist_helper.get_line(self.axes),
                                  color=rcParams['axes.edgecolor'],
                                  fill=False,
                                  linewidth=rcParams['axes.linewidth'],
                                  capstyle=rcParams['lines.solid_capstyle'],
                                  joinstyle=rcParams['lines.solid_joinstyle'],
                                  transform=tran)
        else:
            self.line = axisline_style(self, transform=tran)

    def _draw_line(self, renderer):
        self.line.set_path(self._axis_artist_helper.get_line(self.axes))
        if self.get_axisline_style() is not None:
            self.line.set_line_mutation_scale(self.major_ticklabels.get_size())
        self.line.draw(renderer)

    def _init_ticks(self, **kwargs):
        axis_name = self.axis.axis_name

        trans = (self._axis_artist_helper.get_tick_transform(self.axes) +
                 self.offset_transform)

        self.major_ticks = Ticks(kwargs.get(
            "major_tick_size", rcParams[f"{axis_name}tick.major.size"]),
                                 axis=self.axis,
                                 transform=trans)
        self.minor_ticks = Ticks(kwargs.get(
            "minor_tick_size", rcParams[f"{axis_name}tick.minor.size"]),
                                 axis=self.axis,
                                 transform=trans)

        size = rcParams[f"{axis_name}tick.labelsize"]
        self.major_ticklabels = TickLabels(
            axis=self.axis,
            axis_direction=self._axis_direction,
            figure=self.axes.figure,
            transform=trans,
            fontsize=size,
            pad=kwargs.get("major_tick_pad",
                           rcParams[f"{axis_name}tick.major.pad"]),
        )
        self.minor_ticklabels = TickLabels(
            axis=self.axis,
            axis_direction=self._axis_direction,
            figure=self.axes.figure,
            transform=trans,
            fontsize=size,
            pad=kwargs.get("minor_tick_pad",
                           rcParams[f"{axis_name}tick.minor.pad"]),
        )

    def _get_tick_info(self, tick_iter):
        """
        Return a pair of:

        - list of locs and angles for ticks
        - list of locs, angles and labels for ticklabels.
        """
        ticks_loc_angle = []
        ticklabels_loc_angle_label = []

        ticklabel_add_angle = self._ticklabel_add_angle

        for loc, angle_normal, angle_tangent, label in tick_iter:
            angle_label = angle_tangent - 90 + ticklabel_add_angle
            angle_tick = (angle_normal if 90 <=
                          (angle_label - angle_normal) % 360 <= 270 else
                          angle_normal + 180)
            ticks_loc_angle.append([loc, angle_tick])
            ticklabels_loc_angle_label.append([loc, angle_label, label])

        return ticks_loc_angle, ticklabels_loc_angle_label

    def _update_ticks(self, renderer):
        # set extra pad for major and minor ticklabels: use ticksize of
        # majorticks even for minor ticks. not clear what is best.

        dpi_cor = renderer.points_to_pixels(1.)
        if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
            self.major_ticklabels._set_external_pad(
                self.major_ticks._ticksize * dpi_cor)
            self.minor_ticklabels._set_external_pad(
                self.major_ticks._ticksize * dpi_cor)
        else:
            self.major_ticklabels._set_external_pad(0)
            self.minor_ticklabels._set_external_pad(0)

        majortick_iter, minortick_iter = \
            self._axis_artist_helper.get_tick_iterators(self.axes)

        tick_loc_angle, ticklabel_loc_angle_label = \
            self._get_tick_info(majortick_iter)
        self.major_ticks.set_locs_angles(tick_loc_angle)
        self.major_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)

        tick_loc_angle, ticklabel_loc_angle_label = \
            self._get_tick_info(minortick_iter)
        self.minor_ticks.set_locs_angles(tick_loc_angle)
        self.minor_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)

    def _draw_ticks(self, renderer):
        self._update_ticks(renderer)
        self.major_ticks.draw(renderer)
        self.major_ticklabels.draw(renderer)
        self.minor_ticks.draw(renderer)
        self.minor_ticklabels.draw(renderer)
        if (self.major_ticklabels.get_visible()
                or self.minor_ticklabels.get_visible()):
            self._draw_offsetText(renderer)

    _offsetText_pos = dict(left=(0, 1, "bottom", "right"),
                           right=(1, 1, "bottom", "left"),
                           bottom=(1, 0, "top", "right"),
                           top=(1, 1, "bottom", "right"))

    def _init_offsetText(self, direction):
        x, y, va, ha = self._offsetText_pos[direction]
        self.offsetText = mtext.Annotation(
            "",
            xy=(x, y),
            xycoords="axes fraction",
            xytext=(0, 0),
            textcoords="offset points",
            color=rcParams['xtick.color'],
            horizontalalignment=ha,
            verticalalignment=va,
        )
        self.offsetText.set_transform(IdentityTransform())
        self.axes._set_artist_props(self.offsetText)

    def _update_offsetText(self):
        self.offsetText.set_text(self.axis.major.formatter.get_offset())
        self.offsetText.set_size(self.major_ticklabels.get_size())
        offset = (self.major_ticklabels.get_pad() +
                  self.major_ticklabels.get_size() + 2)
        self.offsetText.xyann = (0, offset)

    def _draw_offsetText(self, renderer):
        self._update_offsetText()
        self.offsetText.draw(renderer)

    def _init_label(self, **kwargs):
        tr = (self._axis_artist_helper.get_axislabel_transform(self.axes) +
              self.offset_transform)
        self.label = AxisLabel(
            0,
            0,
            "__from_axes__",
            color="auto",
            fontsize=kwargs.get("labelsize", rcParams['axes.labelsize']),
            fontweight=rcParams['axes.labelweight'],
            axis=self.axis,
            transform=tr,
            axis_direction=self._axis_direction,
        )
        self.label.set_figure(self.axes.figure)
        labelpad = kwargs.get("labelpad", 5)
        self.label.set_pad(labelpad)

    def _update_label(self, renderer):
        if not self.label.get_visible():
            return

        if self._ticklabel_add_angle != self._axislabel_add_angle:
            if ((self.major_ticks.get_visible()
                 and not self.major_ticks.get_tick_out())
                    or (self.minor_ticks.get_visible()
                        and not self.major_ticks.get_tick_out())):
                axislabel_pad = self.major_ticks._ticksize
            else:
                axislabel_pad = 0
        else:
            axislabel_pad = max(self.major_ticklabels._axislabel_pad,
                                self.minor_ticklabels._axislabel_pad)

        self.label._set_external_pad(axislabel_pad)

        xy, angle_tangent = \
            self._axis_artist_helper.get_axislabel_pos_angle(self.axes)
        if xy is None:
            return

        angle_label = angle_tangent - 90

        x, y = xy
        self.label._set_ref_angle(angle_label + self._axislabel_add_angle)
        self.label.set(x=x, y=y)

    def _draw_label(self, renderer):
        self._update_label(renderer)
        self.label.draw(renderer)

    def set_label(self, s):
        self.label.set_text(s)

    def get_tightbbox(self, renderer):
        if not self.get_visible():
            return
        self._axis_artist_helper.update_lim(self.axes)
        self._update_ticks(renderer)
        self._update_label(renderer)
        bb = [
            *self.major_ticklabels.get_window_extents(renderer),
            *self.minor_ticklabels.get_window_extents(renderer),
            self.label.get_window_extent(renderer),
            self.offsetText.get_window_extent(renderer),
        ]
        bb = [b for b in bb if b and (b.width != 0 or b.height != 0)]
        if bb:
            _bbox = Bbox.union(bb)
            return _bbox
        else:
            return None

    @martist.allow_rasterization
    def draw(self, renderer):
        # docstring inherited
        if not self.get_visible():
            return
        renderer.open_group(__name__, gid=self.get_gid())
        self._axis_artist_helper.update_lim(self.axes)
        self._draw_ticks(renderer)
        self._draw_line(renderer)
        self._draw_label(renderer)
        renderer.close_group(__name__)

    def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
        """
        Toggle visibility of ticks, ticklabels, and (axis) label.
        To turn all off, ::

          axis.toggle(all=False)

        To turn all off but ticks on ::

          axis.toggle(all=False, ticks=True)

        To turn all on but (axis) label off ::

          axis.toggle(all=True, label=False))

        """
        if all:
            _ticks, _ticklabels, _label = True, True, True
        elif all is not None:
            _ticks, _ticklabels, _label = False, False, False
        else:
            _ticks, _ticklabels, _label = None, None, None

        if ticks is not None:
            _ticks = ticks
        if ticklabels is not None:
            _ticklabels = ticklabels
        if label is not None:
            _label = label

        if _ticks is not None:
            self.major_ticks.set_visible(_ticks)
            self.minor_ticks.set_visible(_ticks)
        if _ticklabels is not None:
            self.major_ticklabels.set_visible(_ticklabels)
            self.minor_ticklabels.set_visible(_ticklabels)
        if _label is not None:
            self.label.set_visible(_label)