Beispiel #1
0
    def update_positions(self, renderer):

        x, y = self.xytext
        self._x, self._y = self._get_xy(x, y, self.textcoords)

        x, y = self.xy
        x, y = self._get_xy(x, y, self.xycoords)

        if self.arrowprops:
            x0, y0 = x, y
            l, b, w, h = self.get_window_extent(renderer).get_bounds()
            dpi = self.figure.dpi.get()
            r = l + w
            t = b + h
            xc = 0.5 * (l + r)
            yc = 0.5 * (b + t)
            # pick the x,y corner of the text bbox closest to point
            # annotated
            dsu = [(abs(val - x0), val) for val in l, r, xc]
            dsu.sort()
            d, x = dsu[0]

            dsu = [(abs(val - y0), val) for val in b, t, yc]
            dsu.sort()
            d, y = dsu[0]

            d = self.arrowprops.copy()
            width = d.pop('width', 4)
            headwidth = d.pop('headwidth', 12)
            frac = d.pop('frac', 0.1)
            shrink = d.pop('shrink', 0.0)

            theta = math.atan2(y - y0, x - x0)
            r = math.sqrt((y - y0)**2. + (x - x0)**2.)
            dx = shrink * r * math.cos(theta)
            dy = shrink * r * math.sin(theta)

            self.arrow = YAArrow(self.figure.dpi, (x0 + dx, y0 + dy),
                                 (x - dx, y - dy),
                                 width=width,
                                 headwidth=headwidth,
                                 frac=frac,
                                 **d)
            self.arrow.set_clip_box(self.get_clip_box())
Beispiel #2
0
    def update_positions(self, renderer):

        x, y = self.xytext
        self._x, self._y = self._get_xy(x, y, self.textcoords)


        x, y = self.xy
        x, y = self._get_xy(x, y, self.xycoords)

        if self.arrowprops:
            x0, y0 = x, y
            l,b,w,h = self.get_window_extent(renderer).bounds
            dpi = self.figure.dpi
            r = l+w
            t = b+h
            xc = 0.5*(l+r)
            yc = 0.5*(b+t)
            # pick the x,y corner of the text bbox closest to point
            # annotated
            dsu = [(abs(val-x0), val) for val in l, r, xc]
            dsu.sort()
            d, x = dsu[0]

            dsu = [(abs(val-y0), val) for val in b, t, yc]
            dsu.sort()
            d, y = dsu[0]


            d = self.arrowprops.copy()
            width = d.pop('width', 4)
            headwidth = d.pop('headwidth', 12)
            frac = d.pop('frac', 0.1)
            shrink = d.pop('shrink', 0.0)

            theta = math.atan2(y-y0, x-x0)
            r = math.sqrt((y-y0)**2. + (x-x0)**2.)
            dx = shrink*r*math.cos(theta)
            dy = shrink*r*math.sin(theta)

            self.arrow = YAArrow(self.figure.dpi, (x0+dx,y0+dy), (x-dx, y-dy),
                            width=width, headwidth=headwidth, frac=frac,
                            **d)
            self.arrow.set_clip_box(self.get_clip_box())
Beispiel #3
0
class Annotation(Text):
    """
    A Text class to make annotating things in the figure: Figure,
    Axes, Point, Rectangle, etc... easier
    """
    def __str__(self):
        return "Annotation(%g,%g,%s)"%(self.xy[0],self.xy[1],self._text)
    def __init__(self, s, xy,
                 xytext=None,
                 xycoords='data',
                 textcoords=None,
                 arrowprops=None,
                 **kwargs):
        """
        Annotate the x,y point xy with text s at x,y location xytext
        (xytext if None defaults to xy and textcoords if None defaults
        to xycoords).

        arrowprops, if not None, is a dictionary of line properties
        (see matplotlib.lines.Line2D) for the arrow that connects
        annotation to the point.   Valid keys are

          - width : the width of the arrow in points
          - frac  : the fraction of the arrow length occupied by the head
          - headwidth : the width of the base of the arrow head in points
          - shrink: often times it is convenient to have the arrowtip
            and base a bit away from the text and point being
            annotated.  If d is the distance between the text and
            annotated point, shrink will shorten the arrow so the tip
            and base are shink percent of the distance d away from the
            endpoints.  ie, shrink=0.05 is 5%%
          - any key for matplotlib.patches.polygon

        xycoords and textcoords are strings that indicate the
        coordinates of xy and xytext.

           'figure points'   : points from the lower left corner of the figure
           'figure pixels'   : pixels from the lower left corner of the figure                           'figure fraction' : 0,0 is lower left of figure and 1,1 is upper, right
           'axes points'     : points from lower left corner of axes
           'axes pixels'     : pixels from lower left corner of axes
           'axes fraction'   : 0,1 is lower left of axes and 1,1 is upper right
           'data'            : use the coordinate system of the object being annotated (default)
           'offset points'     : Specify an offset (in points) from the xy value
           'polar'           : you can specify theta, r for the annotation, even
                               in cartesian plots.  Note that if you
                               are using a polar axes, you do not need
                               to specify polar for the coordinate
                               system since that is the native"data" coordinate system.

        If a points or pixels option is specified, values will be
        added to the left, bottom and if negative, values will be
        subtracted from the top, right.  Eg,

          # 10 points to the right of the left border of the axes and
          # 5 points below the top border
          xy=(10,-5), xycoords='axes points'

        Additional kwargs are Text properties:

        %(Text)s

        """
        if xytext is None:
            xytext = xy
        if textcoords is None:
            textcoords = xycoords
        # we'll draw ourself after the artist we annotate by default
        x,y = self.xytext = xytext
        Text.__init__(self, x, y, s, **kwargs)
        self.xy = xy
        self.arrowprops = arrowprops
        self.arrow = None
        self.xycoords = xycoords
        self.textcoords = textcoords
    __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd

    def contains(self,event):
        t,tinfo = Text.contains(self,event)
        if self.arrow is not None:
            a,ainfo=self.arrow.contains(event)
            t = t or a
        return t,tinfo


    def set_clip_box(self, clipbox):
        """
        Set the artist's clip Bbox

        ACCEPTS: a matplotlib.transform.Bbox instance
        """
        Text.set_clip_box(self, clipbox)

    def _get_xy(self, x, y, s):
        if s=='data':
            trans = self.axes.transData
            x = float(self.convert_xunits(x))
            y = float(self.convert_yunits(y))
            return trans.transform_point((x, y))
        elif s=='offset points':
            # convert the data point
            dx, dy = self.xy

            # prevent recursion
            if self.xycoords == 'offset points':
               return self._get_xy(dx, dy, 'data')

            dx, dy = self._get_xy(dx, dy, self.xycoords)

            # convert the offset
            dpi = self.figure.dpi.get()
            x *= dpi/72.
            y *= dpi/72.

            # add the offset to the data point
            x += dx
            y += dy

            return x, y
        elif s=='polar':
            theta, r = x, y
            x = r*npy.cos(theta)
            y = r*npy.sin(theta)
            trans = self.axes.transData
            return trans.transform_point((x,y))
        elif s=='figure points':
            #points from the lower left corner of the figure
            dpi = self.figure.dpi
            l,b,w,h = self.figure.bbox.bounds
            r = l+w
            t = b+h

            x *= dpi/72.
            y *= dpi/72.
            if x<0:
                x = r + x
            if y<0:
                y = t + y
            return x,y
        elif s=='figure pixels':
            #pixels from the lower left corner of the figure
            l,b,w,h = self.figure.bbox.bounds
            r = l+w
            t = b+h
            if x<0:
                x = r + x
            if y<0:
                y = t + y
            return x, y
        elif s=='figure fraction':
            #(0,0) is lower left, (1,1) is upper right of figure
            trans = self.figure.transFigure
            return trans.transform_point((x,y))
        elif s=='axes points':
            #points from the lower left corner of the axes
            dpi = self.figure.dpi
            l,b,w,h = self.axes.bbox.bounds
            r = l+w
            t = b+h
            if x<0:
                x = r + x*dpi/72.
            else:
                x = l + x*dpi/72.
            if y<0:
                y = t + y*dpi/72.
            else:
                y = b + y*dpi/72.
            return x, y
        elif s=='axes pixels':
            #pixels from the lower left corner of the axes

            l,b,w,h = self.axes.bbox.bounds
            r = l+w
            t = b+h
            if x<0:
                x = r + x
            else:
                x = l + x
            if y<0:
                y = t + y
            else:
                y = b + y
            return x, y
        elif s=='axes fraction':
            #(0,0) is lower left, (1,1) is upper right of axes
            trans = self.axes.transAxes
            return trans.transform_point((x, y))


    def update_positions(self, renderer):

        x, y = self.xytext
        self._x, self._y = self._get_xy(x, y, self.textcoords)


        x, y = self.xy
        x, y = self._get_xy(x, y, self.xycoords)

        if self.arrowprops:
            x0, y0 = x, y
            l,b,w,h = self.get_window_extent(renderer).bounds
            dpi = self.figure.dpi
            r = l+w
            t = b+h
            xc = 0.5*(l+r)
            yc = 0.5*(b+t)
            # pick the x,y corner of the text bbox closest to point
            # annotated
            dsu = [(abs(val-x0), val) for val in l, r, xc]
            dsu.sort()
            d, x = dsu[0]

            dsu = [(abs(val-y0), val) for val in b, t, yc]
            dsu.sort()
            d, y = dsu[0]


            d = self.arrowprops.copy()
            width = d.pop('width', 4)
            headwidth = d.pop('headwidth', 12)
            frac = d.pop('frac', 0.1)
            shrink = d.pop('shrink', 0.0)

            theta = math.atan2(y-y0, x-x0)
            r = math.sqrt((y-y0)**2. + (x-x0)**2.)
            dx = shrink*r*math.cos(theta)
            dy = shrink*r*math.sin(theta)

            self.arrow = YAArrow(self.figure.dpi, (x0+dx,y0+dy), (x-dx, y-dy),
                            width=width, headwidth=headwidth, frac=frac,
                            **d)
            self.arrow.set_clip_box(self.get_clip_box())

    def draw(self, renderer):
        self.update_positions(renderer)

        if self.arrow is not None:
            self.arrow.draw(renderer)

        Text.draw(self, renderer)
Beispiel #4
0
class Annotation(Text):
    """
    A Text class to make annotating things in the figure: Figure,
    Axes, Point, Rectangle, etc... easier
    """
    def __str__(self):
        return "Annotation(%g,%g,%s)" % (self.xy[0], self.xy[1], self._text)

    def __init__(self,
                 s,
                 xy,
                 xytext=None,
                 xycoords='data',
                 textcoords=None,
                 arrowprops=None,
                 **kwargs):
        """
        Annotate the x,y point xy with text s at x,y location xytext
        (xytext if None defaults to xy and textcoords if None defaults
        to xycoords).

        arrowprops, if not None, is a dictionary of line properties
        (see matplotlib.lines.Line2D) for the arrow that connects
        annotation to the point.   Valid keys are

          - width : the width of the arrow in points
          - frac  : the fraction of the arrow length occupied by the head
          - headwidth : the width of the base of the arrow head in points
          - shrink: often times it is convenient to have the arrowtip
            and base a bit away from the text and point being
            annotated.  If d is the distance between the text and
            annotated point, shrink will shorten the arrow so the tip
            and base are shink percent of the distance d away from the
            endpoints.  ie, shrink=0.05 is 5%%
          - any key for matplotlib.patches.polygon

        xycoords and textcoords are strings that indicate the
        coordinates of xy and xytext.

           'figure points'   : points from the lower left corner of the figure
           'figure pixels'   : pixels from the lower left corner of the figure                           'figure fraction' : 0,0 is lower left of figure and 1,1 is upper, right
           'axes points'     : points from lower left corner of axes
           'axes pixels'     : pixels from lower left corner of axes
           'axes fraction'   : 0,1 is lower left of axes and 1,1 is upper right
           'data'            : use the coordinate system of the object being annotated (default)
           'offset points'     : Specify an offset (in points) from the xy value
           'polar'           : you can specify theta, r for the annotation, even
                               in cartesian plots.  Note that if you
                               are using a polar axes, you do not need
                               to specify polar for the coordinate
                               system since that is the native"data" coordinate system.

        If a points or pixels option is specified, values will be
        added to the left, bottom and if negative, values will be
        subtracted from the top, right.  Eg,

          # 10 points to the right of the left border of the axes and
          # 5 points below the top border
          xy=(10,-5), xycoords='axes points'

        Additional kwargs are Text properties:

        %(Text)s

        """
        if xytext is None:
            xytext = xy
        if textcoords is None:
            textcoords = xycoords
        # we'll draw ourself after the artist we annotate by default
        x, y = self.xytext = xytext
        Text.__init__(self, x, y, s, **kwargs)
        self.xy = xy
        self.arrowprops = arrowprops
        self.arrow = None
        self.xycoords = xycoords
        self.textcoords = textcoords

    __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd

    def contains(self, event):
        t, tinfo = Text.contains(self, event)
        if self.arrow is not None:
            a, ainfo = self.arrow.contains(event)
            t = t or a
        return t, tinfo

    def set_clip_box(self, clipbox):
        """
        Set the artist's clip Bbox

        ACCEPTS: a matplotlib.transform.Bbox instance
        """
        Text.set_clip_box(self, clipbox)

    def _get_xy(self, x, y, s):
        if s == 'data':
            trans = self.axes.transData
            x = float(self.convert_xunits(x))
            y = float(self.convert_yunits(y))
            return trans.xy_tup((x, y))
        elif s == 'offset points':
            # convert the data point
            dx, dy = self.xy

            # prevent recursion
            if self.xycoords == 'offset points':
                return self._get_xy(dx, dy, 'data')

            dx, dy = self._get_xy(dx, dy, self.xycoords)

            # convert the offset
            dpi = self.figure.dpi.get()
            x *= dpi / 72.
            y *= dpi / 72.

            # add the offset to the data point
            x += dx
            y += dy

            return x, y
        elif s == 'polar':
            theta, r = x, y
            x = r * npy.cos(theta)
            y = r * npy.sin(theta)
            trans = self.axes.transData
            return trans.xy_tup((x, y))
        elif s == 'figure points':
            #points from the lower left corner of the figure
            dpi = self.figure.dpi.get()
            l, b, w, h = self.figure.bbox.get_bounds()
            r = l + w
            t = b + h

            x *= dpi / 72.
            y *= dpi / 72.
            if x < 0:
                x = r + x
            if y < 0:
                y = t + y
            return x, y
        elif s == 'figure pixels':
            #pixels from the lower left corner of the figure
            l, b, w, h = self.figure.bbox.get_bounds()
            r = l + w
            t = b + h
            if x < 0:
                x = r + x
            if y < 0:
                y = t + y
            return x, y
        elif s == 'figure fraction':
            #(0,0) is lower left, (1,1) is upper right of figure
            trans = self.figure.transFigure
            return trans.xy_tup((x, y))
        elif s == 'axes points':
            #points from the lower left corner of the axes
            dpi = self.figure.dpi.get()
            l, b, w, h = self.axes.bbox.get_bounds()
            r = l + w
            t = b + h
            if x < 0:
                x = r + x * dpi / 72.
            else:
                x = l + x * dpi / 72.
            if y < 0:
                y = t + y * dpi / 72.
            else:
                y = b + y * dpi / 72.
            return x, y
        elif s == 'axes pixels':
            #pixels from the lower left corner of the axes

            l, b, w, h = self.axes.bbox.get_bounds()
            r = l + w
            t = b + h
            if x < 0:
                x = r + x
            else:
                x = l + x
            if y < 0:
                y = t + y
            else:
                y = b + y
            return x, y
        elif s == 'axes fraction':
            #(0,0) is lower left, (1,1) is upper right of axes
            trans = self.axes.transAxes
            return trans.xy_tup((x, y))

    def update_positions(self, renderer):

        x, y = self.xytext
        self._x, self._y = self._get_xy(x, y, self.textcoords)

        x, y = self.xy
        x, y = self._get_xy(x, y, self.xycoords)

        if self.arrowprops:
            x0, y0 = x, y
            l, b, w, h = self.get_window_extent(renderer).get_bounds()
            dpi = self.figure.dpi.get()
            r = l + w
            t = b + h
            xc = 0.5 * (l + r)
            yc = 0.5 * (b + t)
            # pick the x,y corner of the text bbox closest to point
            # annotated
            dsu = [(abs(val - x0), val) for val in l, r, xc]
            dsu.sort()
            d, x = dsu[0]

            dsu = [(abs(val - y0), val) for val in b, t, yc]
            dsu.sort()
            d, y = dsu[0]

            d = self.arrowprops.copy()
            width = d.pop('width', 4)
            headwidth = d.pop('headwidth', 12)
            frac = d.pop('frac', 0.1)
            shrink = d.pop('shrink', 0.0)

            theta = math.atan2(y - y0, x - x0)
            r = math.sqrt((y - y0)**2. + (x - x0)**2.)
            dx = shrink * r * math.cos(theta)
            dy = shrink * r * math.sin(theta)

            self.arrow = YAArrow(self.figure.dpi, (x0 + dx, y0 + dy),
                                 (x - dx, y - dy),
                                 width=width,
                                 headwidth=headwidth,
                                 frac=frac,
                                 **d)
            self.arrow.set_clip_box(self.get_clip_box())

    def draw(self, renderer):
        self.update_positions(renderer)

        if self.arrow is not None:
            self.arrow.draw(renderer)

        Text.draw(self, renderer)
Beispiel #5
0
class Annotation(Text):
    """
    A :class:`~matplotlib.text.Text` class to make annotating things
    in the figure, such as :class:`~matplotlib.figure.Figure`,
    :class:`~matplotlib.axes.Axes`,
    :class:`~matplotlib.patches.Rectangle`, etc., easier.
    """

    def __str__(self):
        return "Annotation(%g,%g,%s)" % (self.xy[0], self.xy[1], repr(self._text))

    def __init__(self, s, xy, xytext=None, xycoords="data", textcoords=None, arrowprops=None, **kwargs):
        """
        Annotate the *x*, *y* point *xy* with text *s* at *x*, *y*
        location *xytext*.  (If *xytext* = *None*, defaults to *xy*,
        and if *textcoords* = *None*, defaults to *xycoords*).

        *arrowprops*, if not *None*, is a dictionary of line properties
        (see :class:`matplotlib.lines.Line2D`) for the arrow that connects
        annotation to the point.   Valid keys are

        =========   ===========================================================
           Key                            Description
        =========   ===========================================================
        width       the width of the arrow in points
        frac        the fraction of the arrow length occupied by the head
        headwidth   the width of the base of the arrow head in points
        shrink      oftentimes it is convenient to have the arrowtip
                    and base a bit away from the text and point being
                    annotated.  If *d* is the distance between the text and
                    annotated point, shrink will shorten the arrow so the tip
                    and base are shink percent of the distance *d* away from the
                    endpoints.  ie, ``shrink=0.05 is 5%%``
        ?           any key for :class:`matplotlib.patches.polygon`
        =========   ===========================================================

        *xycoords* and *textcoords* are strings that indicate the
        coordinates of *xy* and *xytext*.

        =================   ===================================================
             Property                           Description
        =================   ===================================================
        'figure points'     points from the lower left corner of the figure
        'figure pixels'     pixels from the lower left corner of the figure
        'figure fraction'   0,0 is lower left of figure and 1,1 is upper, right
        'axes points'       points from lower left corner of axes
        'axes pixels'       pixels from lower left corner of axes
        'axes fraction'     0,1 is lower left of axes and 1,1 is upper right
        'data'              use the coordinate system of the object being
                            annotated (default)
        'offset points'     Specify an offset (in points) from the *xy* value

        'polar'             you can specify *theta*, *r* for the annotation,
                            even in cartesian plots.  Note that if you
                            are using a polar axes, you do not need
                            to specify polar for the coordinate
                            system since that is the native "data" coordinate
                            system.
        =================   ===================================================

        If a 'points' or 'pixels' option is specified, values will be
        added to the bottom-left and if negative, values will be
        subtracted from the top-right.  Eg::

          # 10 points to the right of the left border of the axes and
          # 5 points below the top border
          xy=(10,-5), xycoords='axes points'

        Additional kwargs are Text properties:

        %(Text)s

        """
        if xytext is None:
            xytext = xy
        if textcoords is None:
            textcoords = xycoords
        # we'll draw ourself after the artist we annotate by default
        x, y = self.xytext = xytext
        Text.__init__(self, x, y, s, **kwargs)
        self.xy = xy
        self.arrowprops = arrowprops
        self.arrow = None
        self.xycoords = xycoords
        self.textcoords = textcoords

    __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd

    def contains(self, event):
        t, tinfo = Text.contains(self, event)
        if self.arrow is not None:
            a, ainfo = self.arrow.contains(event)
            t = t or a
        return t, tinfo

    def set_figure(self, fig):

        if self.arrow is not None:
            self.arrow.set_figure(fig)
        Artist.set_figure(self, fig)

    def set_clip_box(self, clipbox):
        """
        Set the artist's clip Bbox

        ACCEPTS: a matplotlib.transform.Bbox instance
        """
        Text.set_clip_box(self, clipbox)

    def _get_xy(self, x, y, s):
        if s == "data":
            trans = self.axes.transData
            x = float(self.convert_xunits(x))
            y = float(self.convert_yunits(y))
            return trans.transform_point((x, y))
        elif s == "offset points":
            # convert the data point
            dx, dy = self.xy

            # prevent recursion
            if self.xycoords == "offset points":
                return self._get_xy(dx, dy, "data")

            dx, dy = self._get_xy(dx, dy, self.xycoords)

            # convert the offset
            dpi = self.figure.get_dpi()
            x *= dpi / 72.0
            y *= dpi / 72.0

            # add the offset to the data point
            x += dx
            y += dy

            return x, y
        elif s == "polar":
            theta, r = x, y
            x = r * np.cos(theta)
            y = r * np.sin(theta)
            trans = self.axes.transData
            return trans.transform_point((x, y))
        elif s == "figure points":
            # points from the lower left corner of the figure
            dpi = self.figure.dpi
            l, b, w, h = self.figure.bbox.bounds
            r = l + w
            t = b + h

            x *= dpi / 72.0
            y *= dpi / 72.0
            if x < 0:
                x = r + x
            if y < 0:
                y = t + y
            return x, y
        elif s == "figure pixels":
            # pixels from the lower left corner of the figure
            l, b, w, h = self.figure.bbox.bounds
            r = l + w
            t = b + h
            if x < 0:
                x = r + x
            if y < 0:
                y = t + y
            return x, y
        elif s == "figure fraction":
            # (0,0) is lower left, (1,1) is upper right of figure
            trans = self.figure.transFigure
            return trans.transform_point((x, y))
        elif s == "axes points":
            # points from the lower left corner of the axes
            dpi = self.figure.dpi
            l, b, w, h = self.axes.bbox.bounds
            r = l + w
            t = b + h
            if x < 0:
                x = r + x * dpi / 72.0
            else:
                x = l + x * dpi / 72.0
            if y < 0:
                y = t + y * dpi / 72.0
            else:
                y = b + y * dpi / 72.0
            return x, y
        elif s == "axes pixels":
            # pixels from the lower left corner of the axes

            l, b, w, h = self.axes.bbox.bounds
            r = l + w
            t = b + h
            if x < 0:
                x = r + x
            else:
                x = l + x
            if y < 0:
                y = t + y
            else:
                y = b + y
            return x, y
        elif s == "axes fraction":
            # (0,0) is lower left, (1,1) is upper right of axes
            trans = self.axes.transAxes
            return trans.transform_point((x, y))

    def update_positions(self, renderer):

        x, y = self.xytext
        self._x, self._y = self._get_xy(x, y, self.textcoords)

        x, y = self.xy
        x, y = self._get_xy(x, y, self.xycoords)

        if self.arrowprops:
            x0, y0 = x, y
            l, b, w, h = self.get_window_extent(renderer).bounds
            r = l + w
            t = b + h
            xc = 0.5 * (l + r)
            yc = 0.5 * (b + t)
            # pick the x,y corner of the text bbox closest to point
            # annotated
            dsu = [(abs(val - x0), val) for val in l, r, xc]
            dsu.sort()
            d, x = dsu[0]

            dsu = [(abs(val - y0), val) for val in b, t, yc]
            dsu.sort()
            d, y = dsu[0]

            d = self.arrowprops.copy()
            width = d.pop("width", 4)
            headwidth = d.pop("headwidth", 12)
            frac = d.pop("frac", 0.1)
            shrink = d.pop("shrink", 0.0)

            theta = math.atan2(y - y0, x - x0)
            r = math.sqrt((y - y0) ** 2.0 + (x - x0) ** 2.0)
            dx = shrink * r * math.cos(theta)
            dy = shrink * r * math.sin(theta)

            self.arrow = YAArrow(
                self.figure, (x0 + dx, y0 + dy), (x - dx, y - dy), width=width, headwidth=headwidth, frac=frac, **d
            )
            self.arrow.set_clip_box(self.get_clip_box())

    def draw(self, renderer):
        self.update_positions(renderer)

        if self.arrow is not None:
            if self.arrow.figure is None and self.figure is not None:
                self.arrow.figure = self.figure
            self.arrow.draw(renderer)

        Text.draw(self, renderer)