Exemple #1
0
class MarkerStyle:
    style_table = """
============================== ===============================================
marker                         description
============================== ===============================================
%s
``'$...$'``                    render the string using mathtext
*verts*                        a list of (x, y) pairs in range (0, 1)
(*numsides*, *style*, *angle*) see below
============================== ===============================================

The marker can also be a tuple (*numsides*, *style*, *angle*), which
will create a custom, regular symbol.

    *numsides*:
      the number of sides

    *style*:
      the style of the regular symbol:

      =====   =============================================
      Value   Description
      =====   =============================================
      0       a regular polygon
      1       a star-like symbol
      2       an asterisk
      3       a circle (*numsides* and *angle* is ignored)
      =====   =============================================

    *angle*:
      the angle of rotation of the symbol

For backward compatibility, the form (*verts*, 0) is also accepted,
but it is equivalent to just *verts* for giving a raw set of vertices
that define the shape.
"""

    # TODO: Automatically generate this
    accepts = """ACCEPTS: [ %s | ``'$...$'`` | *tuple* | *Nx2 array* ]"""

    markers =  {
        '.'        : 'point',
        ','        : 'pixel',
        'o'        : 'circle',
        'v'        : 'triangle_down',
        '^'        : 'triangle_up',
        '<'        : 'triangle_left',
        '>'        : 'triangle_right',
        '1'        : 'tri_down',
        '2'        : 'tri_up',
        '3'        : 'tri_left',
        '4'        : 'tri_right',
        '8'        : 'octagon',
        's'        : 'square',
        'p'        : 'pentagon',
        '*'        : 'star',
        'h'        : 'hexagon1',
        'H'        : 'hexagon2',
        '+'        : 'plus',
        'x'        : 'x',
        'D'        : 'diamond',
        'd'        : 'thin_diamond',
        '|'        : 'vline',
        '_'        : 'hline',
        TICKLEFT   : 'tickleft',
        TICKRIGHT  : 'tickright',
        TICKUP     : 'tickup',
        TICKDOWN   : 'tickdown',
        CARETLEFT  : 'caretleft',
        CARETRIGHT : 'caretright',
        CARETUP    : 'caretup',
        CARETDOWN  : 'caretdown',
        "None"       : 'nothing',
        None       : 'nothing',
        ' '        : 'nothing',
        ''         : 'nothing'
    }

    # Just used for informational purposes.  is_filled()
    # is calculated in the _set_* functions.
    filled_markers = (
        'o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd')

    fillstyles = ('full', 'left' , 'right' , 'bottom' , 'top', 'none')
    _half_fillstyles = ('left' , 'right' , 'bottom' , 'top')

    # TODO: Is this ever used as a non-constant?
    _point_size_reduction = 0.5

    def __init__(self, marker=None, fillstyle='full'):
        self._fillstyle = fillstyle
        self.set_marker(marker)
        self.set_fillstyle(fillstyle)

    def _recache(self):
        self._path = Path(np.empty((0,2)))
        self._transform = IdentityTransform()
        self._alt_path = None
        self._alt_transform = None
        self._snap_threshold = None
        self._joinstyle = 'round'
        self._capstyle = 'butt'
        self._filled = True
        self._marker_function()

    def __nonzero__(self):
        return bool(len(self._path.vertices))

    def is_filled(self):
        return self._filled

    def get_fillstyle(self):
        return self._fillstyle

    def set_fillstyle(self, fillstyle):
        # TODO: Raise exception for markers where fillstyle doesn't make sense
        assert fillstyle in self.fillstyles
        self._fillstyle = fillstyle
        self._recache()

    def get_joinstyle(self):
        return self._joinstyle

    def get_capstyle(self):
        return self._capstyle

    def get_marker(self):
        return self._marker

    def set_marker(self, marker):
        if (iterable(marker) and len(marker) in (2, 3) and
            marker[1] in (0, 1, 2, 3)):
            self._marker_function = self._set_tuple_marker
        elif marker in self.markers:
            self._marker_function = getattr(
                self, '_set_' + self.markers[marker])
        elif is_string_like(marker) and is_math_text(marker):
            self._marker_function = self._set_mathtext_path
        elif isinstance(marker, Path):
            self._marker_function = self._set_path_marker
        else:
            try:
                path = Path(marker)
                self._marker_function = self._set_vertices
            except:
                raise ValueError('Unrecognized marker style %s' % marker)

        self._marker = marker
        self._recache()

    def get_path(self):
        return self._path

    def get_transform(self):
        return self._transform.frozen()

    def get_alt_path(self):
        return self._alt_path

    def get_alt_transform(self):
        return self._alt_transform.frozen()

    def get_snap_threshold(self):
        return self._snap_threshold

    def _set_nothing(self):
        self._filled = False

    def _set_custom_marker(self, path):
        verts = path.vertices
        rescale = max(np.max(np.abs(verts[:,0])), np.max(np.abs(verts[:,1])))
        self._transform = Affine2D().scale(1.0 / rescale)
        self._path = path

    def _set_path_marker(self):
        self._set_custom_marker(self._marker)

    def _set_vertices(self):
        path = Path(verts)
        self._set_custom_marker(path)

    def _set_tuple_marker(self):
        marker = self._marker
        if is_numlike(marker[0]):
            if len(marker) == 2:
                numsides, rotation = marker[0], 0.0
            elif len(marker) == 3:
                numsides, rotation = marker[0], marker[2]
            symstyle = marker[1]
            if symstyle == 0:
                self._path = Path.unit_regular_polygon(numsides)
                self._joinstyle = 'miter'
            elif symstyle == 1:
                self._path = Path.unit_regular_star(numsides)
                self._joinstyle = 'bevel'
            elif symstyle == 2:
                self._path = Path.unit_regular_asterisk(numsides)
                self._filled = False
                self._joinstyle = 'bevel'
            elif symstyle == 3:
                self._path = Path.unit_circle()
            self._transform = Affine2D().scale(0.5).rotate_deg(rotation)
        else:
            verts = np.asarray(marker[0])
            path = Path(verts)
            self._set_custom_marker(path)

    def _set_mathtext_path(self):
        """
        Draws mathtext markers '$...$' using TextPath object.

        Submitted by tcb
        """
        from matplotlib.patches import PathPatch
        from matplotlib.text import TextPath
        from matplotlib.font_manager import FontProperties

        # again, the properties could be initialised just once outside
        # this function
        # Font size is irrelevant here, it will be rescaled based on
        # the drawn size later
        props = FontProperties(size=1.0)
        text = TextPath(xy=(0,0), s=self.get_marker(), fontproperties=props,
                        usetex=rcParams['text.usetex'])
        if len(text.vertices) == 0:
            return

        xmin, ymin = text.vertices.min(axis=0)
        xmax, ymax = text.vertices.max(axis=0)
        width = xmax - xmin
        height = ymax - ymin
        max_dim = max(width, height)
        self._transform = Affine2D() \
            .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \
            .scale(1.0 / max_dim)
        self._path = text
        self._snap = False

    def _half_fill(self):
        fs = self.get_fillstyle()
        result = fs in self._half_fillstyles
        return result

    def _set_circle(self, reduction = 1.0):
        self._transform = Affine2D().scale(0.5 * reduction)
        self._snap_threshold = 3.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_circle()
        else:
            # build a right-half circle
            if fs=='bottom': rotate = 270.
            elif fs=='top': rotate = 90.
            elif fs=='left': rotate = 180.
            else: rotate = 0.

            self._path = self._alt_path = Path.unit_circle_righthalf()
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform.frozen().rotate_deg(180.)

    def _set_pixel(self):
        self._path = Path.unit_rectangle()
        # Ideally, you'd want -0.5, -0.5 here, but then the snapping
        # algorithm in the Agg backend will round this to a 2x2
        # rectangle from (-1, -1) to (1, 1).  By offsetting it
        # slightly, we can force it to be (0, 0) to (1, 1), which both
        # makes it only be a single pixel and places it correctly
        # aligned to 1-width stroking (i.e. the ticks).  This hack is
        # the best of a number of bad alternatives, mainly because the
        # backends are not aware of what marker is actually being used
        # beyond just its path data.
        self._transform = Affine2D().translate(-0.49999, -0.49999)
        self._snap_threshold = None

    def _set_point(self):
        self._set_circle(reduction = self._point_size_reduction)

    _triangle_path = Path(
        [[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    # Going down halfway looks to small.  Golden ratio is too far.
    _triangle_path_u = Path(
        [[0.0, 1.0], [-3/5., -1/5.], [3/5., -1/5.], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_d = Path(
        [[-3/5., -1/5.], [3/5., -1/5.], [1.0, -1.0], [-1.0, -1.0], [-3/5., -1/5.]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_l = Path(
        [[0.0, 1.0], [0.0, -1.0], [-1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_r = Path(
        [[0.0, 1.0], [0.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    def _set_triangle(self, rot, skip):
        self._transform = Affine2D().scale(0.5, 0.5).rotate_deg(rot)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = self._triangle_path
        else:
            mpaths = [self._triangle_path_u,
                      self._triangle_path_l,
                      self._triangle_path_d,
                      self._triangle_path_r]

            if fs=='top':
                self._path = mpaths[(0+skip) % 4]
                self._alt_path = mpaths[(2+skip) % 4]
            elif fs=='bottom':
                self._path = mpaths[(2+skip) % 4]
                self._alt_path = mpaths[(0+skip) % 4]
            elif fs=='left':
                self._path = mpaths[(1+skip) % 4]
                self._alt_path = mpaths[(3+skip) % 4]
            else:
                self._path = mpaths[(3+skip) % 4]
                self._alt_path = mpaths[(1+skip) % 4]

            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_triangle_up(self):
        return self._set_triangle(0.0, 0)

    def _set_triangle_down(self):
        return self._set_triangle(180.0, 2)

    def _set_triangle_left(self):
        return self._set_triangle(90.0, 3)

    def _set_triangle_right(self):
        return self._set_triangle(270.0, 1)

    def _set_square(self):
        self._transform = Affine2D().translate(-0.5, -0.5)
        self._snap_threshold = 2.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            # build a bottom filled square out of two rectangles, one
            # filled.  Use the rotation to support left, right, bottom
            # or top
            if fs=='bottom': rotate = 0.
            elif fs=='top': rotate = 180.
            elif fs=='left': rotate = 270.
            else: rotate = 90.

            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5], [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], [0.0, 1.0], [0.0, 0.5]])
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_diamond(self):
        self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]])

            if fs=='bottom': rotate = 270.
            elif fs=='top': rotate = 90.
            elif fs=='left': rotate = 180.
            else: rotate = 0.

            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_thin_diamond(self):
        self._set_diamond()
        self._transform.scale(0.6, 1.0)

    def _set_pentagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        polypath = Path.unit_regular_polygon(5)
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            y = (1+np.sqrt(5))/4.
            top = Path([verts[0], verts[1], verts[4], verts[0]])
            bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]])
            left = Path([verts[0], verts[1], verts[2], [0,-y], verts[0]])
            right = Path([verts[0], verts[4], verts[3], [0,-y], verts[0]])

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_star(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_star(5, innerCircle=0.381966)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            top = Path(np.vstack((verts[0:4,:], verts[7:10,:], verts[0])))
            bottom = Path(np.vstack((verts[3:8,:], verts[3])))
            left = Path(np.vstack((verts[0:6,:], verts[0])))
            right = Path(np.vstack((verts[0], verts[5:10,:], verts[0])))

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'bevel'

    def _set_hexagon1(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x = np.abs(np.cos(5*np.pi/6.))
            top = Path(np.vstack(([-x,0],verts[(1,0,5),:],[x,0])))
            bottom = Path(np.vstack(([-x,0],verts[2:5,:],[x,0])))
            left = Path(verts[(0,1,2,3),:])
            right = Path(verts[(0,5,4,3),:])

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_hexagon2(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(30)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x, y = np.sqrt(3)/4, 3/4.
            top = Path(verts[(1,0,5,4,1),:])
            bottom = Path(verts[(1,2,3,4),:])
            left = Path(np.vstack(([x,y],verts[(0,1,2),:],[-x,-y],[x,y])))
            right = Path(np.vstack(([x,y],verts[(5,4,3),:],[-x,-y])))

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_octagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(8)

        if not self._half_fill():
            self._transform.rotate_deg(22.5)
            self._path = polypath
        else:
            x = np.sqrt(2.)/4.
            half = Path([[0, -1], [0, 1], [-x, 1], [-1, x],
                         [-1, -x], [-x, -1], [0, -1]])

            if fs=='bottom': rotate = 90.
            elif fs=='top': rotate = 270.
            elif fs=='right': rotate = 180.
            else: rotate = 0.

            self._transform.rotate_deg(rotate)
            self._path = self._alt_path = half
            self._alt_transform = self._transform.frozen().rotate_deg(180.0)

        self._joinstyle = 'miter'

    _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]])
    def _set_vline(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    def _set_hline(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]])
    def _set_tickleft(self):
        self._transform = Affine2D().scale(-1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    def _set_tickright(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]])
    def _set_tickup(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    def _set_tickdown(self):
        self._transform = Affine2D().scale(1.0, -1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    _plus_path = Path([[-1.0, 0.0], [1.0, 0.0],
                       [0.0, -1.0], [0.0, 1.0]],
                      [Path.MOVETO, Path.LINETO,
                       Path.MOVETO, Path.LINETO])
    def _set_plus(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._plus_path

    _tri_path = Path([[0.0, 0.0], [0.0, -1.0],
                      [0.0, 0.0], [0.8, 0.5],
                      [0.0, 0.0], [-0.8, 0.5]],
                     [Path.MOVETO, Path.LINETO,
                      Path.MOVETO, Path.LINETO,
                      Path.MOVETO, Path.LINETO])
    def _set_tri_down(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_up(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_left(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_right(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]])
    def _set_caretdown(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretup(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretleft(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretright(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    _x_path = Path([[-1.0, -1.0], [1.0, 1.0],
                    [-1.0, 1.0], [1.0, -1.0]],
                   [Path.MOVETO, Path.LINETO,
                    Path.MOVETO, Path.LINETO])
    def _set_x(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._x_path
Exemple #2
0
class MarkerStyle:
    style_table = """
============================== ===============================================
marker                         description
============================== ===============================================
%s
``'$...$'``                    render the string using mathtext.
*verts*                        a list of (x, y) pairs used for Path vertices.
path                           a :class:`~matplotlib.path.Path` instance.
(*numsides*, *style*, *angle*) see below
============================== ===============================================

The marker can also be a tuple (*numsides*, *style*, *angle*), which
will create a custom, regular symbol.

    *numsides*:
      the number of sides

    *style*:
      the style of the regular symbol:

      =====   =============================================
      Value   Description
      =====   =============================================
      0       a regular polygon
      1       a star-like symbol
      2       an asterisk
      3       a circle (*numsides* and *angle* is ignored)
      =====   =============================================

    *angle*:
      the angle of rotation of the symbol, in degrees

For backward compatibility, the form (*verts*, 0) is also accepted,
but it is equivalent to just *verts* for giving a raw set of vertices
that define the shape.
"""

    # TODO: Automatically generate this
    accepts = """ACCEPTS: [ %s | ``'$...$'`` | *tuple* | *Nx2 array* ]"""

    markers = {
        '.': 'point',
        ',': 'pixel',
        'o': 'circle',
        'v': 'triangle_down',
        '^': 'triangle_up',
        '<': 'triangle_left',
        '>': 'triangle_right',
        '1': 'tri_down',
        '2': 'tri_up',
        '3': 'tri_left',
        '4': 'tri_right',
        '8': 'octagon',
        's': 'square',
        'p': 'pentagon',
        '*': 'star',
        'h': 'hexagon1',
        'H': 'hexagon2',
        '+': 'plus',
        'x': 'x',
        'D': 'diamond',
        'd': 'thin_diamond',
        '|': 'vline',
        '_': 'hline',
        TICKLEFT: 'tickleft',
        TICKRIGHT: 'tickright',
        TICKUP: 'tickup',
        TICKDOWN: 'tickdown',
        CARETLEFT: 'caretleft',
        CARETRIGHT: 'caretright',
        CARETUP: 'caretup',
        CARETDOWN: 'caretdown',
        "None": 'nothing',
        None: 'nothing',
        ' ': 'nothing',
        '': 'nothing'
    }

    # Just used for informational purposes.  is_filled()
    # is calculated in the _set_* functions.
    filled_markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H',
                      'D', 'd')

    fillstyles = ('full', 'left', 'right', 'bottom', 'top', 'none')
    _half_fillstyles = ('left', 'right', 'bottom', 'top')

    # TODO: Is this ever used as a non-constant?
    _point_size_reduction = 0.5

    def __init__(self, marker=None, fillstyle='full'):
        self._fillstyle = fillstyle
        self.set_marker(marker)
        self.set_fillstyle(fillstyle)

    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop('_marker_function')
        return d

    def __setstate__(self, statedict):
        self.__dict__ = statedict
        self.set_marker(self._marker)
        self._recache()

    def _recache(self):
        self._path = Path(np.empty((0, 2)))
        self._transform = IdentityTransform()
        self._alt_path = None
        self._alt_transform = None
        self._snap_threshold = None
        self._joinstyle = 'round'
        self._capstyle = 'butt'
        self._filled = True
        self._marker_function()

    def __nonzero__(self):
        return bool(len(self._path.vertices))

    def is_filled(self):
        return self._filled

    def get_fillstyle(self):
        return self._fillstyle

    def set_fillstyle(self, fillstyle):
        # TODO: Raise exception for markers where fillstyle doesn't make sense
        assert fillstyle in self.fillstyles
        self._fillstyle = fillstyle
        self._recache()

    def get_joinstyle(self):
        return self._joinstyle

    def get_capstyle(self):
        return self._capstyle

    def get_marker(self):
        return self._marker

    def set_marker(self, marker):
        if (iterable(marker) and len(marker) in (2, 3)
                and marker[1] in (0, 1, 2, 3)):
            self._marker_function = self._set_tuple_marker
        elif isinstance(marker, np.ndarray):
            self._marker_function = self._set_vertices
        elif not isinstance(marker, list) and marker in self.markers:
            self._marker_function = getattr(self,
                                            '_set_' + self.markers[marker])
        elif is_string_like(marker) and is_math_text(marker):
            self._marker_function = self._set_mathtext_path
        elif isinstance(marker, Path):
            self._marker_function = self._set_path_marker
        else:
            try:
                _ = Path(marker)
                self._marker_function = self._set_vertices
            except ValueError:
                raise ValueError('Unrecognized marker style {}'.format(marker))

        self._marker = marker
        self._recache()

    def get_path(self):
        return self._path

    def get_transform(self):
        return self._transform.frozen()

    def get_alt_path(self):
        return self._alt_path

    def get_alt_transform(self):
        return self._alt_transform.frozen()

    def get_snap_threshold(self):
        return self._snap_threshold

    def _set_nothing(self):
        self._filled = False

    def _set_custom_marker(self, path):
        verts = path.vertices
        rescale = max(np.max(np.abs(verts[:, 0])), np.max(np.abs(verts[:, 1])))
        self._transform = Affine2D().scale(1.0 / rescale)
        self._path = path

    def _set_path_marker(self):
        self._set_custom_marker(self._marker)

    def _set_vertices(self):
        verts = self._marker
        marker = Path(verts)
        self._set_custom_marker(marker)

    def _set_tuple_marker(self):
        marker = self._marker
        if is_numlike(marker[0]):
            if len(marker) == 2:
                numsides, rotation = marker[0], 0.0
            elif len(marker) == 3:
                numsides, rotation = marker[0], marker[2]
            symstyle = marker[1]
            if symstyle == 0:
                self._path = Path.unit_regular_polygon(numsides)
                self._joinstyle = 'miter'
            elif symstyle == 1:
                self._path = Path.unit_regular_star(numsides)
                self._joinstyle = 'bevel'
            elif symstyle == 2:
                self._path = Path.unit_regular_asterisk(numsides)
                self._filled = False
                self._joinstyle = 'bevel'
            elif symstyle == 3:
                self._path = Path.unit_circle()
            self._transform = Affine2D().scale(0.5).rotate_deg(rotation)
        else:
            verts = np.asarray(marker[0])
            path = Path(verts)
            self._set_custom_marker(path)

    def _set_mathtext_path(self):
        """
        Draws mathtext markers '$...$' using TextPath object.

        Submitted by tcb
        """
        from matplotlib.text import TextPath
        from matplotlib.font_manager import FontProperties

        # again, the properties could be initialised just once outside
        # this function
        # Font size is irrelevant here, it will be rescaled based on
        # the drawn size later
        props = FontProperties(size=1.0)
        text = TextPath(xy=(0, 0),
                        s=self.get_marker(),
                        fontproperties=props,
                        usetex=rcParams['text.usetex'])
        if len(text.vertices) == 0:
            return

        xmin, ymin = text.vertices.min(axis=0)
        xmax, ymax = text.vertices.max(axis=0)
        width = xmax - xmin
        height = ymax - ymin
        max_dim = max(width, height)
        self._transform = Affine2D() \
            .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \
            .scale(1.0 / max_dim)
        self._path = text
        self._snap = False

    def _half_fill(self):
        fs = self.get_fillstyle()
        result = fs in self._half_fillstyles
        return result

    def _set_circle(self, reduction=1.0):
        self._transform = Affine2D().scale(0.5 * reduction)
        self._snap_threshold = 6.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_circle()
        else:
            # build a right-half circle
            if fs == 'bottom': rotate = 270.
            elif fs == 'top': rotate = 90.
            elif fs == 'left': rotate = 180.
            else: rotate = 0.

            self._path = self._alt_path = Path.unit_circle_righthalf()
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform.frozen().rotate_deg(180.)

    def _set_pixel(self):
        self._path = Path.unit_rectangle()
        # Ideally, you'd want -0.5, -0.5 here, but then the snapping
        # algorithm in the Agg backend will round this to a 2x2
        # rectangle from (-1, -1) to (1, 1).  By offsetting it
        # slightly, we can force it to be (0, 0) to (1, 1), which both
        # makes it only be a single pixel and places it correctly
        # aligned to 1-width stroking (i.e. the ticks).  This hack is
        # the best of a number of bad alternatives, mainly because the
        # backends are not aware of what marker is actually being used
        # beyond just its path data.
        self._transform = Affine2D().translate(-0.49999, -0.49999)
        self._snap_threshold = None

    def _set_point(self):
        self._set_circle(reduction=self._point_size_reduction)

    _triangle_path = Path(
        [[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    # Going down halfway looks to small.  Golden ratio is too far.
    _triangle_path_u = Path(
        [[0.0, 1.0], [-3 / 5., -1 / 5.], [3 / 5., -1 / 5.], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_d = Path(
        [[-3 / 5., -1 / 5.], [3 / 5., -1 / 5.], [1.0, -1.0], [-1.0, -1.0],
         [-3 / 5., -1 / 5.]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_l = Path(
        [[0.0, 1.0], [0.0, -1.0], [-1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
    _triangle_path_r = Path(
        [[0.0, 1.0], [0.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])

    def _set_triangle(self, rot, skip):
        self._transform = Affine2D().scale(0.5, 0.5).rotate_deg(rot)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = self._triangle_path
        else:
            mpaths = [
                self._triangle_path_u, self._triangle_path_l,
                self._triangle_path_d, self._triangle_path_r
            ]

            if fs == 'top':
                self._path = mpaths[(0 + skip) % 4]
                self._alt_path = mpaths[(2 + skip) % 4]
            elif fs == 'bottom':
                self._path = mpaths[(2 + skip) % 4]
                self._alt_path = mpaths[(0 + skip) % 4]
            elif fs == 'left':
                self._path = mpaths[(1 + skip) % 4]
                self._alt_path = mpaths[(3 + skip) % 4]
            else:
                self._path = mpaths[(3 + skip) % 4]
                self._alt_path = mpaths[(1 + skip) % 4]

            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_triangle_up(self):
        return self._set_triangle(0.0, 0)

    def _set_triangle_down(self):
        return self._set_triangle(180.0, 2)

    def _set_triangle_left(self):
        return self._set_triangle(90.0, 3)

    def _set_triangle_right(self):
        return self._set_triangle(270.0, 1)

    def _set_square(self):
        self._transform = Affine2D().translate(-0.5, -0.5)
        self._snap_threshold = 2.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            # build a bottom filled square out of two rectangles, one
            # filled.  Use the rotation to support left, right, bottom
            # or top
            if fs == 'bottom': rotate = 0.
            elif fs == 'top': rotate = 180.
            elif fs == 'left': rotate = 270.
            else: rotate = 90.

            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5],
                               [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0],
                                   [0.0, 1.0], [0.0, 0.5]])
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_diamond(self):
        self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.0], [0.0, 1.0], [1.0, 1.0],
                                   [0.0, 0.0]])

            if fs == 'bottom': rotate = 270.
            elif fs == 'top': rotate = 90.
            elif fs == 'left': rotate = 180.
            else: rotate = 0.

            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_thin_diamond(self):
        self._set_diamond()
        self._transform.scale(0.6, 1.0)

    def _set_pentagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        polypath = Path.unit_regular_polygon(5)
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            y = (1 + np.sqrt(5)) / 4.
            top = Path([verts[0], verts[1], verts[4], verts[0]])
            bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]])
            left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]])
            right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]])

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_star(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_star(5, innerCircle=0.381966)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            top = Path(np.vstack((verts[0:4, :], verts[7:10, :], verts[0])))
            bottom = Path(np.vstack((verts[3:8, :], verts[3])))
            left = Path(np.vstack((verts[0:6, :], verts[0])))
            right = Path(np.vstack((verts[0], verts[5:10, :], verts[0])))

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'bevel'

    def _set_hexagon1(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = None

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x = np.abs(np.cos(5 * np.pi / 6.))
            top = Path(np.vstack(([-x, 0], verts[(1, 0, 5), :], [x, 0])))
            bottom = Path(np.vstack(([-x, 0], verts[2:5, :], [x, 0])))
            left = Path(verts[(0, 1, 2, 3), :])
            right = Path(verts[(0, 5, 4, 3), :])

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_hexagon2(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(30)
        self._snap_threshold = None

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x, y = np.sqrt(3) / 4, 3 / 4.
            top = Path(verts[(1, 0, 5, 4, 1), :])
            bottom = Path(verts[(1, 2, 3, 4), :])
            left = Path(
                np.vstack(([x, y], verts[(0, 1, 2), :], [-x, -y], [x, y])))
            right = Path(np.vstack(([x, y], verts[(5, 4, 3), :], [-x, -y])))

            if fs == 'top':
                mpath, mpath_alt = top, bottom
            elif fs == 'bottom':
                mpath, mpath_alt = bottom, top
            elif fs == 'left':
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = 'miter'

    def _set_octagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(8)

        if not self._half_fill():
            self._transform.rotate_deg(22.5)
            self._path = polypath
        else:
            x = np.sqrt(2.) / 4.
            half = Path([[0, -1], [0, 1], [-x, 1], [-1, x], [-1, -x], [-x, -1],
                         [0, -1]])

            if fs == 'bottom': rotate = 90.
            elif fs == 'top': rotate = 270.
            elif fs == 'right': rotate = 180.
            else: rotate = 0.

            self._transform.rotate_deg(rotate)
            self._path = self._alt_path = half
            self._alt_transform = self._transform.frozen().rotate_deg(180.0)

        self._joinstyle = 'miter'

    _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]])

    def _set_vline(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    def _set_hline(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]])

    def _set_tickleft(self):
        self._transform = Affine2D().scale(-1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    def _set_tickright(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]])

    def _set_tickup(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    def _set_tickdown(self):
        self._transform = Affine2D().scale(1.0, -1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    _plus_path = Path([[-1.0, 0.0], [1.0, 0.0], [0.0, -1.0], [0.0, 1.0]],
                      [Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO])

    def _set_plus(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._plus_path

    _tri_path = Path([[0.0, 0.0], [0.0, -1.0], [0.0, 0.0], [0.8, 0.5],
                      [0.0, 0.0], [-0.8, 0.5]], [
                          Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO,
                          Path.MOVETO, Path.LINETO
                      ])

    def _set_tri_down(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_up(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_left(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_right(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]])

    def _set_caretdown(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretup(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretleft(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    def _set_caretright(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = 'miter'

    _x_path = Path([[-1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], [1.0, -1.0]],
                   [Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO])

    def _set_x(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._x_path
Exemple #3
0
class MarkerStyle(object):

    markers = {
        ".": "point",
        ",": "pixel",
        "o": "circle",
        "v": "triangle_down",
        "^": "triangle_up",
        "<": "triangle_left",
        ">": "triangle_right",
        "1": "tri_down",
        "2": "tri_up",
        "3": "tri_left",
        "4": "tri_right",
        "8": "octagon",
        "s": "square",
        "p": "pentagon",
        "*": "star",
        "h": "hexagon1",
        "H": "hexagon2",
        "+": "plus",
        "x": "x",
        "D": "diamond",
        "d": "thin_diamond",
        "|": "vline",
        "_": "hline",
        TICKLEFT: "tickleft",
        TICKRIGHT: "tickright",
        TICKUP: "tickup",
        TICKDOWN: "tickdown",
        CARETLEFT: "caretleft",
        CARETRIGHT: "caretright",
        CARETUP: "caretup",
        CARETDOWN: "caretdown",
        "None": "nothing",
        None: "nothing",
        " ": "nothing",
        "": "nothing",
    }

    # Just used for informational purposes.  is_filled()
    # is calculated in the _set_* functions.
    filled_markers = ("o", "v", "^", "<", ">", "8", "s", "p", "*", "h", "H", "D", "d")

    fillstyles = ("full", "left", "right", "bottom", "top", "none")
    _half_fillstyles = ("left", "right", "bottom", "top")

    # TODO: Is this ever used as a non-constant?
    _point_size_reduction = 0.5

    def __init__(self, marker=None, fillstyle="full"):
        """
        MarkerStyle

        Attributes
        ----------
        markers : list of known markes

        fillstyles : list of known fillstyles

        filled_markers : list of known filled markers.

        Parameters
        ----------
        marker : string or array_like, optional, default: None
            See the descriptions of possible markers in the module docstring.

        fillstyle : string, optional, default: 'full'
            'full', 'left", 'right', 'bottom', 'top', 'none'
        """
        self._fillstyle = fillstyle
        self.set_marker(marker)
        self.set_fillstyle(fillstyle)

    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop("_marker_function")
        return d

    def __setstate__(self, statedict):
        self.__dict__ = statedict
        self.set_marker(self._marker)
        self._recache()

    def _recache(self):
        self._path = Path(np.empty((0, 2)))
        self._transform = IdentityTransform()
        self._alt_path = None
        self._alt_transform = None
        self._snap_threshold = None
        self._joinstyle = "round"
        self._capstyle = "butt"
        self._filled = True
        self._marker_function()

    def __nonzero__(self):
        return bool(len(self._path.vertices))

    def is_filled(self):
        return self._filled

    def get_fillstyle(self):
        return self._fillstyle

    def set_fillstyle(self, fillstyle):
        """
        Sets fillstyle

        Parameters
        ----------
        fillstyle : string amongst known fillstyles
        """
        if fillstyle not in self.fillstyles:
            raise ValueError("Unrecognized fillstyle %s" % " ".join(self.fillstyles))
        self._fillstyle = fillstyle
        self._recache()

    def get_joinstyle(self):
        return self._joinstyle

    def get_capstyle(self):
        return self._capstyle

    def get_marker(self):
        return self._marker

    def set_marker(self, marker):
        if iterable(marker) and len(marker) in (2, 3) and marker[1] in (0, 1, 2, 3):
            self._marker_function = self._set_tuple_marker
        elif isinstance(marker, np.ndarray):
            self._marker_function = self._set_vertices
        elif marker in self.markers:
            self._marker_function = getattr(self, "_set_" + self.markers[marker])
        elif is_string_like(marker) and is_math_text(marker):
            self._marker_function = self._set_mathtext_path
        elif isinstance(marker, Path):
            self._marker_function = self._set_path_marker
        else:
            try:
                Path(marker)
                self._marker_function = self._set_vertices
            except ValueError:
                raise ValueError("Unrecognized marker style {}".format(marker))

        self._marker = marker
        self._recache()

    def get_path(self):
        return self._path

    def get_transform(self):
        return self._transform.frozen()

    def get_alt_path(self):
        return self._alt_path

    def get_alt_transform(self):
        return self._alt_transform.frozen()

    def get_snap_threshold(self):
        return self._snap_threshold

    def _set_nothing(self):
        self._filled = False

    def _set_custom_marker(self, path):
        verts = path.vertices
        rescale = max(np.max(np.abs(verts[:, 0])), np.max(np.abs(verts[:, 1])))
        self._transform = Affine2D().scale(1.0 / rescale)
        self._path = path

    def _set_path_marker(self):
        self._set_custom_marker(self._marker)

    def _set_vertices(self):
        verts = self._marker
        marker = Path(verts)
        self._set_custom_marker(marker)

    def _set_tuple_marker(self):
        marker = self._marker
        if is_numlike(marker[0]):
            if len(marker) == 2:
                numsides, rotation = marker[0], 0.0
            elif len(marker) == 3:
                numsides, rotation = marker[0], marker[2]
            symstyle = marker[1]
            if symstyle == 0:
                self._path = Path.unit_regular_polygon(numsides)
                self._joinstyle = "miter"
            elif symstyle == 1:
                self._path = Path.unit_regular_star(numsides)
                self._joinstyle = "bevel"
            elif symstyle == 2:
                self._path = Path.unit_regular_asterisk(numsides)
                self._filled = False
                self._joinstyle = "bevel"
            elif symstyle == 3:
                self._path = Path.unit_circle()
            self._transform = Affine2D().scale(0.5).rotate_deg(rotation)
        else:
            verts = np.asarray(marker[0])
            path = Path(verts)
            self._set_custom_marker(path)

    def _set_mathtext_path(self):
        """
        Draws mathtext markers '$...$' using TextPath object.

        Submitted by tcb
        """
        from matplotlib.text import TextPath
        from matplotlib.font_manager import FontProperties

        # again, the properties could be initialised just once outside
        # this function
        # Font size is irrelevant here, it will be rescaled based on
        # the drawn size later
        props = FontProperties(size=1.0)
        text = TextPath(xy=(0, 0), s=self.get_marker(), fontproperties=props, usetex=rcParams["text.usetex"])
        if len(text.vertices) == 0:
            return

        xmin, ymin = text.vertices.min(axis=0)
        xmax, ymax = text.vertices.max(axis=0)
        width = xmax - xmin
        height = ymax - ymin
        max_dim = max(width, height)
        self._transform = Affine2D().translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height).scale(1.0 / max_dim)
        self._path = text
        self._snap = False

    def _half_fill(self):
        fs = self.get_fillstyle()
        result = fs in self._half_fillstyles
        return result

    def _set_circle(self, reduction=1.0):
        self._transform = Affine2D().scale(0.5 * reduction)
        self._snap_threshold = 6.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_circle()
        else:
            # build a right-half circle
            if fs == "bottom":
                rotate = 270.0
            elif fs == "top":
                rotate = 90.0
            elif fs == "left":
                rotate = 180.0
            else:
                rotate = 0.0

            self._path = self._alt_path = Path.unit_circle_righthalf()
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform.frozen().rotate_deg(180.0)

    def _set_pixel(self):
        self._path = Path.unit_rectangle()
        # Ideally, you'd want -0.5, -0.5 here, but then the snapping
        # algorithm in the Agg backend will round this to a 2x2
        # rectangle from (-1, -1) to (1, 1).  By offsetting it
        # slightly, we can force it to be (0, 0) to (1, 1), which both
        # makes it only be a single pixel and places it correctly
        # aligned to 1-width stroking (i.e. the ticks).  This hack is
        # the best of a number of bad alternatives, mainly because the
        # backends are not aware of what marker is actually being used
        # beyond just its path data.
        self._transform = Affine2D().translate(-0.49999, -0.49999)
        self._snap_threshold = None

    def _set_point(self):
        self._set_circle(reduction=self._point_size_reduction)

    _triangle_path = Path(
        [[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]], [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]
    )
    # Going down halfway looks to small.  Golden ratio is too far.
    _triangle_path_u = Path(
        [[0.0, 1.0], [-3 / 5.0, -1 / 5.0], [3 / 5.0, -1 / 5.0], [0.0, 1.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY],
    )
    _triangle_path_d = Path(
        [[-3 / 5.0, -1 / 5.0], [3 / 5.0, -1 / 5.0], [1.0, -1.0], [-1.0, -1.0], [-3 / 5.0, -1 / 5.0]],
        [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY],
    )
    _triangle_path_l = Path(
        [[0.0, 1.0], [0.0, -1.0], [-1.0, -1.0], [0.0, 1.0]], [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]
    )
    _triangle_path_r = Path(
        [[0.0, 1.0], [0.0, -1.0], [1.0, -1.0], [0.0, 1.0]], [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]
    )

    def _set_triangle(self, rot, skip):
        self._transform = Affine2D().scale(0.5, 0.5).rotate_deg(rot)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = self._triangle_path
        else:
            mpaths = [self._triangle_path_u, self._triangle_path_l, self._triangle_path_d, self._triangle_path_r]

            if fs == "top":
                self._path = mpaths[(0 + skip) % 4]
                self._alt_path = mpaths[(2 + skip) % 4]
            elif fs == "bottom":
                self._path = mpaths[(2 + skip) % 4]
                self._alt_path = mpaths[(0 + skip) % 4]
            elif fs == "left":
                self._path = mpaths[(1 + skip) % 4]
                self._alt_path = mpaths[(3 + skip) % 4]
            else:
                self._path = mpaths[(3 + skip) % 4]
                self._alt_path = mpaths[(1 + skip) % 4]

            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_triangle_up(self):
        return self._set_triangle(0.0, 0)

    def _set_triangle_down(self):
        return self._set_triangle(180.0, 2)

    def _set_triangle_left(self):
        return self._set_triangle(90.0, 3)

    def _set_triangle_right(self):
        return self._set_triangle(270.0, 1)

    def _set_square(self):
        self._transform = Affine2D().translate(-0.5, -0.5)
        self._snap_threshold = 2.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            # build a bottom filled square out of two rectangles, one
            # filled.  Use the rotation to support left, right, bottom
            # or top
            if fs == "bottom":
                rotate = 0.0
            elif fs == "top":
                rotate = 180.0
            elif fs == "left":
                rotate = 270.0
            else:
                rotate = 90.0

            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5], [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], [0.0, 1.0], [0.0, 0.5]])
            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_diamond(self):
        self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45)
        self._snap_threshold = 5.0
        fs = self.get_fillstyle()
        if not self._half_fill():
            self._path = Path.unit_rectangle()
        else:
            self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
            self._alt_path = Path([[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]])

            if fs == "bottom":
                rotate = 270.0
            elif fs == "top":
                rotate = 90.0
            elif fs == "left":
                rotate = 180.0
            else:
                rotate = 0.0

            self._transform.rotate_deg(rotate)
            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_thin_diamond(self):
        self._set_diamond()
        self._transform.scale(0.6, 1.0)

    def _set_pentagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        polypath = Path.unit_regular_polygon(5)
        fs = self.get_fillstyle()

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            y = (1 + np.sqrt(5)) / 4.0
            top = Path([verts[0], verts[1], verts[4], verts[0]])
            bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]])
            left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]])
            right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]])

            if fs == "top":
                mpath, mpath_alt = top, bottom
            elif fs == "bottom":
                mpath, mpath_alt = bottom, top
            elif fs == "left":
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_star(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_star(5, innerCircle=0.381966)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            top = Path(np.vstack((verts[0:4, :], verts[7:10, :], verts[0])))
            bottom = Path(np.vstack((verts[3:8, :], verts[3])))
            left = Path(np.vstack((verts[0:6, :], verts[0])))
            right = Path(np.vstack((verts[0], verts[5:10, :], verts[0])))

            if fs == "top":
                mpath, mpath_alt = top, bottom
            elif fs == "bottom":
                mpath, mpath_alt = bottom, top
            elif fs == "left":
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left
            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = "bevel"

    def _set_hexagon1(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x = np.abs(np.cos(5 * np.pi / 6.0))
            top = Path(np.vstack(([-x, 0], verts[(1, 0, 5), :], [x, 0])))
            bottom = Path(np.vstack(([-x, 0], verts[2:5, :], [x, 0])))
            left = Path(verts[(0, 1, 2, 3), :])
            right = Path(verts[(0, 5, 4, 3), :])

            if fs == "top":
                mpath, mpath_alt = top, bottom
            elif fs == "bottom":
                mpath, mpath_alt = bottom, top
            elif fs == "left":
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_hexagon2(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(30)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(6)

        if not self._half_fill():
            self._path = polypath
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x, y = np.sqrt(3) / 4, 3 / 4.0
            top = Path(verts[(1, 0, 5, 4, 1), :])
            bottom = Path(verts[(1, 2, 3, 4), :])
            left = Path(np.vstack(([x, y], verts[(0, 1, 2), :], [-x, -y], [x, y])))
            right = Path(np.vstack(([x, y], verts[(5, 4, 3), :], [-x, -y])))

            if fs == "top":
                mpath, mpath_alt = top, bottom
            elif fs == "bottom":
                mpath, mpath_alt = bottom, top
            elif fs == "left":
                mpath, mpath_alt = left, right
            else:
                mpath, mpath_alt = right, left

            self._path = mpath
            self._alt_path = mpath_alt
            self._alt_transform = self._transform

        self._joinstyle = "miter"

    def _set_octagon(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0

        fs = self.get_fillstyle()
        polypath = Path.unit_regular_polygon(8)

        if not self._half_fill():
            self._transform.rotate_deg(22.5)
            self._path = polypath
        else:
            x = np.sqrt(2.0) / 4.0
            half = Path([[0, -1], [0, 1], [-x, 1], [-1, x], [-1, -x], [-x, -1], [0, -1]])

            if fs == "bottom":
                rotate = 90.0
            elif fs == "top":
                rotate = 270.0
            elif fs == "right":
                rotate = 180.0
            else:
                rotate = 0.0

            self._transform.rotate_deg(rotate)
            self._path = self._alt_path = half
            self._alt_transform = self._transform.frozen().rotate_deg(180.0)

        self._joinstyle = "miter"

    _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]])

    def _set_vline(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    def _set_hline(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._line_marker_path

    _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]])

    def _set_tickleft(self):
        self._transform = Affine2D().scale(-1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    def _set_tickright(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickhoriz_path

    _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]])

    def _set_tickup(self):
        self._transform = Affine2D().scale(1.0, 1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    def _set_tickdown(self):
        self._transform = Affine2D().scale(1.0, -1.0)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._tickvert_path

    _plus_path = Path(
        [[-1.0, 0.0], [1.0, 0.0], [0.0, -1.0], [0.0, 1.0]], [Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO]
    )

    def _set_plus(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 1.0
        self._filled = False
        self._path = self._plus_path

    _tri_path = Path(
        [[0.0, 0.0], [0.0, -1.0], [0.0, 0.0], [0.8, 0.5], [0.0, 0.0], [-0.8, 0.5]],
        [Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO],
    )

    def _set_tri_down(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_up(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_left(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    def _set_tri_right(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 5.0
        self._filled = False
        self._path = self._tri_path

    _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]])

    def _set_caretdown(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = "miter"

    def _set_caretup(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(180)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = "miter"

    def _set_caretleft(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(270)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = "miter"

    def _set_caretright(self):
        self._transform = Affine2D().scale(0.5).rotate_deg(90)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._caret_path
        self._joinstyle = "miter"

    _x_path = Path(
        [[-1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], [1.0, -1.0]], [Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO]
    )

    def _set_x(self):
        self._transform = Affine2D().scale(0.5)
        self._snap_threshold = 3.0
        self._filled = False
        self._path = self._x_path