"""
This module contains all the 2D line class which can draw with a
variety of line styles, markers and colors.
"""

# TODO: expose cap and join style attrs
from __future__ import division

import numpy as np
from numpy import ma
from matplotlib import verbose
import artist
from artist import Artist
from cbook import iterable, is_string_like, is_numlike, ls_mapper, dedent,\
flatten, is_math_text
from colors import colorConverter
from path import Path
from transforms import Affine2D, Bbox, TransformedPath, IdentityTransform

from matplotlib import rcParams
from artist import allow_rasterization
from matplotlib import docstring
from matplotlib.font_manager import FontProperties

# special-purpose marker identifiers:
(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN,
    CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN) = range(8)


def segment_hits(cx, cy, x, y, radius):
    """
    Determine if any line segments are within radius of a
    point. Returns the list of line segments that are within that
    radius.
    """
    # Process single points specially
    if len(x) < 2:
        res, = np.nonzero( (cx - x)**2 + (cy - y)**2 <= radius**2 )
        return res

    # We need to lop the last element off a lot.
    xr,yr = x[:-1],y[:-1]

    # Only look at line segments whose nearest point to C on the line
    # lies within the segment.
    dx,dy = x[1:]-xr, y[1:]-yr
    Lnorm_sq = dx**2+dy**2    # Possibly want to eliminate Lnorm==0
    u = ( (cx-xr)*dx + (cy-yr)*dy )/Lnorm_sq
    candidates = (u>=0) & (u<=1)
    #if any(candidates): print "candidates",xr[candidates]

    # Note that there is a little area near one side of each point
    # which will be near neither segment, and another which will
    # be near both, depending on the angle of the lines.  The
    # following radius test eliminates these ambiguities.
    point_hits = (cx - x)**2 + (cy - y)**2 <= radius**2
    #if any(point_hits): print "points",xr[candidates]
    candidates = candidates & ~(point_hits[:-1] | point_hits[1:])

    # For those candidates which remain, determine how far they lie away
    # from the line.
    px,py = xr+u*dx,yr+u*dy
    line_hits = (cx-px)**2 + (cy-py)**2 <= radius**2
    #if any(line_hits): print "lines",xr[candidates]
    line_hits = line_hits & candidates
    points, = point_hits.ravel().nonzero()
    lines, = line_hits.ravel().nonzero()
    #print points,lines
    return np.concatenate((points,lines))

class Line2D(Artist):
    """
    A line - the line can have both a solid linestyle connecting all
    the vertices, and a marker at each vertex.  Additionally, the
    drawing of the solid line is influenced by the drawstyle, eg one
    can create "stepped" lines in various styles.


    """
    lineStyles = _lineStyles =  { # hidden names deprecated
        '-'          : '_draw_solid',
        '--'         : '_draw_dashed',
        '-.'         : '_draw_dash_dot',
        ':'          : '_draw_dotted',
        'None'       : '_draw_nothing',
        ' '          : '_draw_nothing',
        ''           : '_draw_nothing',
    }

    _drawStyles_l = {
        'default'    : '_draw_lines',
        'steps-mid'  : '_draw_steps_mid',
        'steps-pre'  : '_draw_steps_pre',
        'steps-post' : '_draw_steps_post',
    }

    _drawStyles_s = {
        'steps'      : '_draw_steps_pre',
    }
    drawStyles = {}
    drawStyles.update(_drawStyles_l)
    drawStyles.update(_drawStyles_s)
    # Need a list ordered with long names first:
    drawStyleKeys = _drawStyles_l.keys() + _drawStyles_s.keys()

    markers = _markers =  {  # hidden names deprecated
        '.'  : '_draw_point',
        ','  : '_draw_pixel',
        'o'  : '_draw_circle',
        'v'  : '_draw_triangle_down',
        '^'  : '_draw_triangle_up',
        '<'  : '_draw_triangle_left',
        '>'  : '_draw_triangle_right',
        '1'  : '_draw_tri_down',
        '2'  : '_draw_tri_up',
        '3'  : '_draw_tri_left',
        '4'  : '_draw_tri_right',
        's'  : '_draw_square',
        'p'  : '_draw_pentagon',
        '*'  : '_draw_star',
        'h'  : '_draw_hexagon1',
        'H'  : '_draw_hexagon2',
        '+'  : '_draw_plus',
        'x'  : '_draw_x',
        'D'  : '_draw_diamond',
        'd'  : '_draw_thin_diamond',
        '|'  : '_draw_vline',
        '_'  : '_draw_hline',
        TICKLEFT    : '_draw_tickleft',
        TICKRIGHT   : '_draw_tickright',
        TICKUP      : '_draw_tickup',
        TICKDOWN    : '_draw_tickdown',
        CARETLEFT   : '_draw_caretleft',
        CARETRIGHT  : '_draw_caretright',
        CARETUP     : '_draw_caretup',
        CARETDOWN   : '_draw_caretdown',
        'None' : '_draw_nothing',
        ' ' : '_draw_nothing',
        '' : '_draw_nothing',
    }

    filled_markers = ('o', '^', 'v', '<', '>',
                      's', 'd', 'D', 'h', 'H', 'p', '*')

    fillStyles = ('full', 'left' , 'right' , 'bottom' , 'top')

    zorder = 2
    validCap = ('butt', 'round', 'projecting')
    validJoin =   ('miter', 'round', 'bevel')

    def __str__(self):
        if self._label != "":
            return "Line2D(%s)"%(self._label)
        elif hasattr(self, '_x') and len(self._x) > 3:
            return "Line2D((%g,%g),(%g,%g),...,(%g,%g))"\
                %(self._x[0],self._y[0],self._x[0],self._y[0],self._x[-1],self._y[-1])
        elif hasattr(self, '_x'):
            return "Line2D(%s)"\
                %(",".join(["(%g,%g)"%(x,y) for x,y in zip(self._x,self._y)]))
        else:
            return "Line2D()"

    def __init__(self, xdata, ydata,
                 linewidth       = None, # all Nones default to rc
                 linestyle       = None,
                 color           = None,
                 marker          = None,
                 markersize      = None,
                 markeredgewidth = None,
                 markeredgecolor = None,
                 markerfacecolor = None,
                 markerfacecoloralt = 'none',
                 fillstyle       = 'full',
                 antialiased     = None,
                 dash_capstyle   = None,
                 solid_capstyle  = None,
                 dash_joinstyle  = None,
                 solid_joinstyle = None,
                 pickradius      = 5,
                 drawstyle       = None,
                 markevery       = None,
                 **kwargs
                 ):
        """
        Create a :class:`~matplotlib.lines.Line2D` instance with *x*
        and *y* data in sequences *xdata*, *ydata*.

        The kwargs are :class:`~matplotlib.lines.Line2D` properties:

        %(Line2D)s

        See :meth:`set_linestyle` for a decription of the line styles,
        :meth:`set_marker` for a description of the markers, and
        :meth:`set_drawstyle` for a description of the draw styles.

        """
        Artist.__init__(self)

        #convert sequences to numpy arrays
        if not iterable(xdata):
            raise RuntimeError('xdata must be a sequence')
        if not iterable(ydata):
            raise RuntimeError('ydata must be a sequence')

        if linewidth is None   : linewidth=rcParams['lines.linewidth']

        if linestyle is None   : linestyle=rcParams['lines.linestyle']
        if marker is None      : marker=rcParams['lines.marker']
        if color is None       : color=rcParams['lines.color']

        if markersize is None  : markersize=rcParams['lines.markersize']
        if antialiased is None : antialiased=rcParams['lines.antialiased']
        if dash_capstyle is None : dash_capstyle=rcParams['lines.dash_capstyle']
        if dash_joinstyle is None : dash_joinstyle=rcParams['lines.dash_joinstyle']
        if solid_capstyle is None : solid_capstyle=rcParams['lines.solid_capstyle']
        if solid_joinstyle is None : solid_joinstyle=rcParams['lines.solid_joinstyle']

        if drawstyle is None : drawstyle='default'

        self.set_dash_capstyle(dash_capstyle)
        self.set_dash_joinstyle(dash_joinstyle)
        self.set_solid_capstyle(solid_capstyle)
        self.set_solid_joinstyle(solid_joinstyle)


        self.set_linestyle(linestyle)
        self.set_drawstyle(drawstyle)
        self.set_linewidth(linewidth)
        self.set_color(color)
        self.set_marker(marker)
        self.set_markevery(markevery)
        self.set_antialiased(antialiased)
        self.set_markersize(markersize)
        self._dashSeq = None


        self.set_markerfacecolor(markerfacecolor)
        self.set_markerfacecoloralt(markerfacecoloralt)
        self.set_markeredgecolor(markeredgecolor)
        self.set_markeredgewidth(markeredgewidth)
        self.set_fillstyle(fillstyle)

        self._point_size_reduction = 0.5

        self.verticalOffset = None

        # update kwargs before updating data to give the caller a
        # chance to init axes (and hence unit support)
        self.update(kwargs)
        self.pickradius = pickradius
        if is_numlike(self._picker):
            self.pickradius = self._picker

        self._xorig = np.asarray([])
        self._yorig = np.asarray([])
        self._invalidx = True
        self._invalidy = True
        self.set_data(xdata, ydata)

    def contains(self, mouseevent):
        """
        Test whether the mouse event occurred on the line.  The pick
        radius determines the precision of the location test (usually
        within five points of the value).  Use
        :meth:`~matplotlib.lines.Line2D.get_pickradius` or
        :meth:`~matplotlib.lines.Line2D.set_pickradius` to view or
        modify it.

        Returns *True* if any values are within the radius along with
        ``{'ind': pointlist}``, where *pointlist* is the set of points
        within the radius.

        TODO: sort returned indices by distance
        """
        if callable(self._contains): return self._contains(self,mouseevent)

        if not is_numlike(self.pickradius):
            raise ValueError,"pick radius should be a distance"

        # Make sure we have data to plot
        if self._invalidy or self._invalidx:
            self.recache()
        if len(self._xy)==0: return False,{}

        # Convert points to pixels
        path, affine = self._transformed_path.get_transformed_path_and_affine()
        path = affine.transform_path(path)
        xy = path.vertices
        xt = xy[:, 0]
        yt = xy[:, 1]

        # Convert pick radius from points to pixels
        if self.figure == None:
            warning.warn('no figure set when check if mouse is on line')
            pixels = self.pickradius
        else:
            pixels = self.figure.dpi/72. * self.pickradius

        # Check for collision
        if self._linestyle in ['None',None]:
            # If no line, return the nearby point(s)
            d = (xt-mouseevent.x)**2 + (yt-mouseevent.y)**2
            ind, = np.nonzero(np.less_equal(d, pixels**2))
        else:
            # If line, return the nearby segment(s)
            ind = segment_hits(mouseevent.x,mouseevent.y,xt,yt,pixels)

        ind += self.ind_offset

        # Debugging message
        if False and self._label != u'':
            print "Checking line",self._label,"at",mouseevent.x,mouseevent.y
            print 'xt', xt
            print 'yt', yt
            #print 'dx,dy', (xt-mouseevent.x)**2., (yt-mouseevent.y)**2.
            print 'ind',ind

        # Return the point(s) within radius
        return len(ind)>0,dict(ind=ind)

    def get_pickradius(self):
        'return the pick radius used for containment tests'
        return self.pickradius

    def set_pickradius(self,d):
        """Sets the pick radius used for containment tests

        ACCEPTS: float distance in points
        """
        self.pickradius = d

    def get_fillstyle(self):
        """
        return the marker fillstyle
        """
        return self._fillstyle

    def set_fillstyle(self, fs):
        """
        Set the marker fill style; 'full' means fill the whole marker.
        The other options are for half filled markers

        ACCEPTS: ['full' | 'left' | 'right' | 'bottom' | 'top']
        """
        assert fs in self.fillStyles
        self._fillstyle = fs

    def set_markevery(self, every):
        """
        Set the markevery property to subsample the plot when using
        markers.  Eg if ``markevery=5``, every 5-th marker will be
        plotted.  *every* can be

        None
            Every point will be plotted

        an integer N
            Every N-th marker will be plotted starting with marker 0

        A length-2 tuple of integers
            every=(start, N) will start at point start and plot every N-th marker


        ACCEPTS: None | integer | (startind, stride)

        """
        self._markevery = every

    def get_markevery(self):
        'return the markevery setting'
        return self._markevery

    def set_picker(self,p):
        """Sets the event picker details for the line.

        ACCEPTS: float distance in points or callable pick function
        ``fn(artist, event)``
        """
        if callable(p):
            self._contains = p
        else:
            self.pickradius = p
        self._picker = p

    def get_window_extent(self, renderer):
        bbox = Bbox.unit()
        bbox.update_from_data_xy(self.get_transform().transform(self.get_xydata()),
                                 ignore=True)
        # correct for marker size, if any
        if self._marker is not None:
            ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5
            bbox = bbox.padded(ms)
        return bbox

    def set_axes(self, ax):
        Artist.set_axes(self, ax)
        if ax.xaxis is not None:
            self._xcid = ax.xaxis.callbacks.connect('units', self.recache_always)
        if ax.yaxis is not None:
            self._ycid = ax.yaxis.callbacks.connect('units', self.recache_always)
    set_axes.__doc__ = Artist.set_axes.__doc__

    def set_data(self, *args):
        """
        Set the x and y data

        ACCEPTS: 2D array (rows are x, y) or two 1D arrays
        """
        if len(args)==1:
            x, y = args[0]
        else:
            x, y = args

        self.set_xdata(x)
        self.set_ydata(y)

    def recache_always(self):
        self.recache(always=True)

    def recache(self, always=False):
        if always or self._invalidx:
            xconv = self.convert_xunits(self._xorig)
            if ma.isMaskedArray(self._xorig):
                x = ma.asarray(xconv, float)
            else:
                x = np.asarray(xconv, float)
            x = x.ravel()
        else:
            x = self._x
        if always or self._invalidy:
            yconv = self.convert_yunits(self._yorig)
            if ma.isMaskedArray(self._yorig):
                y = ma.asarray(yconv, float)
            else:
                y = np.asarray(yconv, float)
            y = y.ravel()
        else:
            y = self._y

        if len(x)==1 and len(y)>1:
            x = x * np.ones(y.shape, float)
        if len(y)==1 and len(x)>1:
            y = y * np.ones(x.shape, float)

        if len(x) != len(y):
            raise RuntimeError('xdata and ydata must be the same length')

        x = x.reshape((len(x), 1))
        y = y.reshape((len(y), 1))

        if ma.isMaskedArray(x) or ma.isMaskedArray(y):
            self._xy = ma.concatenate((x, y), 1)
        else:
            self._xy = np.concatenate((x, y), 1)
        self._x = self._xy[:, 0] # just a view
        self._y = self._xy[:, 1] # just a view

        self._subslice = False
        if (self.axes and len(x) > 100 and self._is_sorted(x) and
            self.axes.name == 'rectilinear' and
            self.axes.get_xscale() == 'linear' and
            self._markevery is None):
            self._subslice = True
        if hasattr(self, '_path'):
            interpolation_steps = self._path._interpolation_steps
        else:
            interpolation_steps = 1
        self._path = Path(self._xy, None, interpolation_steps)
        self._transformed_path = None
        self._invalidx = False
        self._invalidy = False

    def _transform_path(self, subslice=None):
        # Masked arrays are now handled by the Path class itself
        if subslice is not None:
            _path = Path(self._xy[subslice,:])
        else:
            _path = self._path
        self._transformed_path = TransformedPath(_path, self.get_transform())


    def set_transform(self, t):
        """
        set the Transformation instance used by this artist

        ACCEPTS: a :class:`matplotlib.transforms.Transform` instance
        """
        Artist.set_transform(self, t)
        self._invalidx = True
        self._invalidy = True

    def _is_sorted(self, x):
        "return true if x is sorted"
        if len(x)<2: return 1
        return np.alltrue(x[1:]-x[0:-1]>=0)

    @allow_rasterization
    def draw(self, renderer):
        if self._invalidy or self._invalidx:
            self.recache()
        self.ind_offset = 0  # Needed for contains() method.
        if self._subslice and self.axes:
            # Need to handle monotonically decreasing case also...
            x0, x1 = self.axes.get_xbound()
            i0, = self._x.searchsorted([x0], 'left')
            i1, = self._x.searchsorted([x1], 'right')
            subslice = slice(max(i0-1, 0), i1+1)
            self.ind_offset = subslice.start
            self._transform_path(subslice)
        if self._transformed_path is None:
            self._transform_path()

        if not self.get_visible(): return

        renderer.open_group('line2d', self.get_gid())
        gc = renderer.new_gc()
        self._set_gc_clip(gc)

        gc.set_foreground(self._color)
        gc.set_antialiased(self._antialiased)
        gc.set_linewidth(self._linewidth)
        gc.set_alpha(self._alpha)
        if self.is_dashed():
            cap = self._dashcapstyle
            join = self._dashjoinstyle
        else:
            cap = self._solidcapstyle
            join = self._solidjoinstyle
        gc.set_joinstyle(join)
        gc.set_capstyle(cap)
        gc.set_snap(self.get_snap())

        funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
        if funcname != '_draw_nothing':
            tpath, affine = self._transformed_path.get_transformed_path_and_affine()
            if len(tpath.vertices):
                self._lineFunc = getattr(self, funcname)
                funcname = self.drawStyles.get(self._drawstyle, '_draw_lines')
                drawFunc = getattr(self, funcname)
                drawFunc(renderer, gc, tpath, affine.frozen())

        if self._marker is not None:
            gc = renderer.new_gc()
            self._set_gc_clip(gc)
            gc.set_foreground(self.get_markeredgecolor())
            gc.set_linewidth(self._markeredgewidth)
            gc.set_alpha(self._alpha)
            funcname = self._markerFunc
            if funcname != '_draw_nothing':
                tpath, affine = self._transformed_path.get_transformed_points_and_affine()
                if len(tpath.vertices):
                    # subsample the markers if markevery is not None
                    markevery = self.get_markevery()
                    if markevery is not None:
                        if iterable(markevery):
                            startind, stride = markevery
                        else:
                            startind, stride = 0, markevery
                        if tpath.codes is not None:
                            codes = tpath.codes[startind::stride]
                        else:
                            codes = None
                        vertices = tpath.vertices[startind::stride]
                        subsampled = Path(vertices, codes)
                    else:
                        subsampled = tpath

                    markerFunc = getattr(self, funcname)
                    markerFunc(renderer, gc, subsampled, affine.frozen())
            gc.restore()

        gc.restore()
        renderer.close_group('line2d')

    def get_antialiased(self): return self._antialiased
    def get_color(self): return self._color
    def get_drawstyle(self): return self._drawstyle
    def get_linestyle(self): return self._linestyle

    def get_linewidth(self): return self._linewidth
    def get_marker(self): return self._marker

    def get_markeredgecolor(self):
        if (is_string_like(self._markeredgecolor) and
            self._markeredgecolor == 'auto'):
            if (self._marker in self.filled_markers or
                is_math_text(self._marker)):
                return 'k'
            else:
                return self._color
        else:
            return self._markeredgecolor


        return self._markeredgecolor
    def get_markeredgewidth(self): return self._markeredgewidth

    def _get_markerfacecolor(self, alt=False):
        if alt:
            fc = self._markerfacecoloralt
        else:
            fc = self._markerfacecolor

        if (fc is None or (is_string_like(fc) and fc.lower()=='none') ):
            return fc
        elif (is_string_like(fc) and fc.lower() == 'auto'):
            return self._color
        else:
            return fc

    def get_markerfacecolor(self):
        return self._get_markerfacecolor(alt=False)

    def get_markerfacecoloralt(self):
        return self._get_markerfacecolor(alt=True)

    def get_markersize(self): return self._markersize

    def get_data(self, orig=True):
        """
        Return the xdata, ydata.

        If *orig* is *True*, return the original data
        """
        return self.get_xdata(orig=orig), self.get_ydata(orig=orig)


    def get_xdata(self, orig=True):
        """
        Return the xdata.

        If *orig* is *True*, return the original data, else the
        processed data.
        """
        if orig:
            return self._xorig
        if self._invalidx:
            self.recache()
        return self._x

    def get_ydata(self, orig=True):
        """
        Return the ydata.

        If *orig* is *True*, return the original data, else the
        processed data.
        """
        if orig:
            return self._yorig
        if self._invalidy:
            self.recache()
        return self._y

    def get_path(self):
        """
        Return the :class:`~matplotlib.path.Path` object associated
        with this line.
        """
        if self._invalidy or self._invalidx:
            self.recache()
        return self._path

    def get_xydata(self):
        """
        Return the *xy* data as a Nx2 numpy array.
        """
        if self._invalidy or self._invalidx:
            self.recache()
        return self._xy

    def set_antialiased(self, b):
        """
        True if line should be drawin with antialiased rendering

        ACCEPTS: [True | False]
        """
        self._antialiased = b

    def set_color(self, color):
        """
        Set the color of the line

        ACCEPTS: any matplotlib color
        """
        self._color = color

    def set_drawstyle(self, drawstyle):
        """
        Set the drawstyle of the plot

        'default' connects the points with lines. The steps variants
        produce step-plots. 'steps' is equivalent to 'steps-pre' and
        is maintained for backward-compatibility.

        ACCEPTS: [ 'default' | 'steps' | 'steps-pre' | 'steps-mid' | 'steps-post' ]
        """
        self._drawstyle = drawstyle

    def set_linewidth(self, w):
        """
        Set the line width in points

        ACCEPTS: float value in points
        """
        self._linewidth = w

    def set_linestyle(self, linestyle):
        """
        Set the linestyle of the line (also accepts drawstyles)


        ================    =================
        linestyle           description
        ================    =================
        ``'-'``             solid
        ``'--'``            dashed
        ``'-.'``            dash_dot
        ``':'``             dotted
        ``'None'``          draw nothing
        ``' '``             draw nothing
        ``''``              draw nothing
        ================    =================

        'steps' is equivalent to 'steps-pre' and is maintained for
        backward-compatibility.

        .. seealso::

            :meth:`set_drawstyle`
               To set the drawing style (stepping) of the plot.

        ACCEPTS: [ ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'None'`` | ``' '`` | ``''`` ]
        and any drawstyle in combination with a linestyle, e.g. ``'steps--'``.
        """

        for ds in self.drawStyleKeys:  # long names are first in the list
            if linestyle.startswith(ds):
                self.set_drawstyle(ds)
                if len(linestyle) > len(ds):
                    linestyle = linestyle[len(ds):]
                else:
                    linestyle = '-'
                break

        if linestyle not in self._lineStyles:
            if linestyle in ls_mapper:
                linestyle = ls_mapper[linestyle]
            else:
                verbose.report('Unrecognized line style %s, %s' %
                                            (linestyle, type(linestyle)))
        if linestyle in [' ','']:
            linestyle = 'None'
        self._linestyle = linestyle

    def set_marker(self, marker):
        """
        Set the line marker

        ========== ==========================
        marker     description
        ========== ==========================
        ``'.'``    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
        ``'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
        ``' '``    nothing
        ``''``     nothing
        '$...$'    render the string using mathtext
        ========== ==========================



        ACCEPTS: [ ``'+'`` | ``'*'`` | ``','`` | ``'.'``
                 | ``'1'`` | ``'2'`` | ``'3'`` | ``'4'``
                 | ``'<'`` | ``'>'`` | ``'D'`` | ``'H'``
                 | ``'^'`` | ``'_'`` | ``'d'`` | ``'h'``
                 | ``'o'`` | ``'p'`` | ``'s'`` | ``'v'``
                 | ``'x'`` | ``'|'``
                 | TICKUP | TICKDOWN | TICKLEFT | TICKRIGHT
                 | CARETUP | CARETDOWN | CARETLEFT | CARETRIGHT
                 | ``'None'`` | ``' '`` | ``''`` | '$...$']

        """
        if marker in self._markers:
            self._marker = marker
            self._markerFunc = self._markers[marker]
        elif is_math_text(marker):
            self._marker = marker
            self._markerFunc = '_draw_mathtext_path'
        else: #already handle ' ', '' in marker list
            verbose.report('Unrecognized marker style %s, %s' %
                                            (marker, type(marker)))

    def set_markeredgecolor(self, ec):
        """
        Set the marker edge color

        ACCEPTS: any matplotlib color
        """
        if ec is None :
            ec = 'auto'
        self._markeredgecolor = ec

    def set_markeredgewidth(self, ew):
        """
        Set the marker edge width in points

        ACCEPTS: float value in points
        """
        if ew is None :
            ew = rcParams['lines.markeredgewidth']
        self._markeredgewidth = ew

    def set_markerfacecolor(self, fc):
        """
        Set the marker face color.

        ACCEPTS: any matplotlib color
        """
        if fc is None:
            fc = 'auto'

        self._markerfacecolor = fc

    def set_markerfacecoloralt(self, fc):
        """
        Set the alternate marker face color.

        ACCEPTS: any matplotlib color
        """
        if fc is None:
            fc = 'auto'

        self._markerfacecoloralt = fc

    def set_markersize(self, sz):
        """
        Set the marker size in points

        ACCEPTS: float
        """
        self._markersize = sz

    def set_xdata(self, x):
        """
        Set the data np.array for x

        ACCEPTS: 1D array
        """
        self._xorig = x
        self._invalidx = True

    def set_ydata(self, y):
        """
        Set the data np.array for y

        ACCEPTS: 1D array
        """
        self._yorig = y
        self._invalidy = True

    def set_dashes(self, seq):
        """
        Set the dash sequence, sequence of dashes with on off ink in
        points.  If seq is empty or if seq = (None, None), the
        linestyle will be set to solid.

        ACCEPTS: sequence of on/off ink in points
        """
        if seq == (None, None) or len(seq)==0:
            self.set_linestyle('-')
        else:
            self.set_linestyle('--')
        self._dashSeq = seq  # TODO: offset ignored for now


    def _draw_lines(self, renderer, gc, path, trans):
        self._lineFunc(renderer, gc, path, trans)


    def _draw_mathtext_path(self, renderer, gc, path, trans):
        """
        Draws mathtext markers '$...$' using TextPath object.

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

        gc.set_snap(False)

        # 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)
        path_trans = Affine2D() \
            .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \
            .scale((renderer.points_to_pixels(self.get_markersize()) / max_dim))

        rgbFace = self._get_rgb_face()
        renderer.draw_markers(gc, text, path_trans, path, trans, rgbFace)


    def _draw_steps_pre(self, renderer, gc, path, trans):
        vertices = self._xy
        steps = ma.zeros((2*len(vertices)-1, 2), np.float_)

        steps[0::2, 0], steps[1::2, 0] = vertices[:, 0], vertices[:-1, 0]
        steps[0::2, 1], steps[1:-1:2, 1] = vertices[:, 1], vertices[1:, 1]

        path = Path(steps)
        path = path.transformed(self.get_transform())
        self._lineFunc(renderer, gc, path, IdentityTransform())


    def _draw_steps_post(self, renderer, gc, path, trans):
        vertices = self._xy
        steps = ma.zeros((2*len(vertices)-1, 2), np.float_)

        steps[::2, 0], steps[1:-1:2, 0] = vertices[:, 0], vertices[1:, 0]
        steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:-1, 1]

        path = Path(steps)
        path = path.transformed(self.get_transform())
        self._lineFunc(renderer, gc, path, IdentityTransform())


    def _draw_steps_mid(self, renderer, gc, path, trans):
        vertices = self._xy
        steps = ma.zeros((2*len(vertices), 2), np.float_)

        steps[1:-1:2, 0] = 0.5 * (vertices[:-1, 0] + vertices[1:, 0])
        steps[2::2, 0] = 0.5 * (vertices[:-1, 0] + vertices[1:, 0])
        steps[0, 0] = vertices[0, 0]
        steps[-1, 0] = vertices[-1, 0]
        steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:, 1]

        path = Path(steps)
        path = path.transformed(self.get_transform())
        self._lineFunc(renderer, gc, path, IdentityTransform())


    def _draw_nothing(self, *args, **kwargs):
        pass


    def _draw_solid(self, renderer, gc, path, trans):
        gc.set_linestyle('solid')
        renderer.draw_path(gc, path, trans)


    def _draw_dashed(self, renderer, gc, path, trans):
        gc.set_linestyle('dashed')
        if self._dashSeq is not None:
            gc.set_dashes(0, self._dashSeq)

        renderer.draw_path(gc, path, trans)


    def _draw_dash_dot(self, renderer, gc, path, trans):
        gc.set_linestyle('dashdot')
        renderer.draw_path(gc, path, trans)


    def _draw_dotted(self, renderer, gc, path, trans):
        gc.set_linestyle('dotted')
        renderer.draw_path(gc, path, trans)


    def _draw_point(self, renderer, gc, path, path_trans):
        # just like _draw_circle
        gc.set_snap(renderer.points_to_pixels(self._markersize) > 3.0)
        w = renderer.points_to_pixels(self._markersize) * \
            self._point_size_reduction * 0.5
        transform = Affine2D().scale(w)
        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        if fs=='full':
            renderer.draw_markers(
                gc, Path.unit_circle(), transform, path, path_trans, rgbFace)
        else:
            rgbFace_alt = self._get_rgb_face(alt=True)
            # build a right-half circle
            if fs=='bottom': rotate = 270.
            elif fs=='top': rotate = 90.
            elif fs=='left': rotate = 180.
            else: rotate = 0.

            righthalf = Path.unit_circle_righthalf()
            transform = transform.rotate_deg(rotate)
            renderer.draw_markers(gc, righthalf, transform,
                                  path, path_trans, rgbFace)
            transform = transform.rotate_deg(180.)
            renderer.draw_markers(gc, righthalf, transform,
                                  path, path_trans, rgbFace_alt)


    _draw_pixel_transform = Affine2D().translate(-0.5, -0.5)
    def _draw_pixel(self, renderer, gc, path, path_trans):
        gc.set_snap(False)
        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        # There is no visible difference, so always paint it 'full'
        renderer.draw_markers(gc, Path.unit_rectangle(),
                              self._draw_pixel_transform,
                              path, path_trans, rgbFace)


    def _draw_circle(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) > 3.0)
        w = renderer.points_to_pixels(self._markersize) * 0.5
        transform = Affine2D().scale(w, w)
        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        if fs=='full':
            renderer.draw_markers(gc, Path.unit_circle(), transform,
                                  path, path_trans, rgbFace)
        else:
            rgbFace_alt = self._get_rgb_face(alt=True)
            # build a right-half circle
            if fs=='bottom': rotate = 270.
            elif fs=='top': rotate = 90.
            elif fs=='left': rotate = 180.
            else: rotate = 0.

            righthalf = Path.unit_circle_righthalf()
            transform = transform.rotate_deg(rotate)
            renderer.draw_markers(gc, righthalf, transform,
                                  path, path_trans, rgbFace)
            transform = transform.rotate_deg(180.)
            renderer.draw_markers(gc, righthalf, transform,
                                  path, path_trans, rgbFace_alt)


    _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 _draw_triangle(self, renderer, gc, path, path_trans, direction):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        assert direction in ['up', 'down', 'left', 'right']
        if direction == 'up':
            x,y = offset, offset
            rot = 0.0
            skip = 0
        elif direction == 'down':
            x,y = offset, offset
            rot = 180.0
            skip = 2
        elif direction == 'left':
            x,y = offset, offset
            rot = 90.0
            skip = 3
        else:
            x,y = offset, offset
            rot = 270.0
            skip = 1
        transform = Affine2D().scale(x,y).rotate_deg(rot)
        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()

        if fs=='full':
            renderer.draw_markers(gc, self._triangle_path, transform,
                                  path, path_trans, rgbFace)
        else:
            rgbFace_alt = self._get_rgb_face(alt=True)

            mpaths = [self._triangle_path_u,
                      self._triangle_path_l,
                      self._triangle_path_d,
                      self._triangle_path_r]

            if fs=='top':
                mpath     = mpaths[(0+skip) % 4]
                mpath_alt = mpaths[(2+skip) % 4]
            elif fs=='bottom':
                mpath     = mpaths[(2+skip) % 4]
                mpath_alt = mpaths[(0+skip) % 4]
            elif fs=='left':
                mpath     = mpaths[(1+skip) % 4]
                mpath_alt = mpaths[(3+skip) % 4]
            else:
                mpath     = mpaths[(3+skip) % 4]
                mpath_alt = mpaths[(1+skip) % 4]

            renderer.draw_markers(gc, mpath, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, mpath_alt, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_triangle_up(self, renderer, gc, path, path_trans):
        self._draw_triangle(renderer, gc, path, path_trans, 'up')


    def _draw_triangle_down(self, renderer, gc, path, path_trans):
        self._draw_triangle(renderer, gc, path, path_trans, 'down')


    def _draw_triangle_left(self, renderer, gc, path, path_trans):
        self._draw_triangle(renderer, gc, path, path_trans, 'left')


    def _draw_triangle_right(self, renderer, gc, path, path_trans):
        self._draw_triangle(renderer, gc, path, path_trans, 'right')


    def _draw_square(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 2.0)
        side = renderer.points_to_pixels(self._markersize)
        transform = Affine2D().translate(-0.5, -0.5).scale(side)
        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        if fs=='full':
            renderer.draw_markers(gc, Path.unit_rectangle(), transform,
                                  path, path_trans, rgbFace)
        else:
            rgbFace_alt = self._get_rgb_face(alt=True)
            # 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.

            bottom = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5], [0.0, 0.0]])
            top = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], [0.0, 1.0], [0.0, 0.5]])
            transform = transform.rotate_deg(rotate)
            renderer.draw_markers(gc, bottom, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, top, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_diamond(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        side = renderer.points_to_pixels(self._markersize)
        transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45).scale(side)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        if fs=='full':
            renderer.draw_markers(gc, Path.unit_rectangle(), transform,
                                  path, path_trans, rgbFace)
        else:
            right = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
            left = 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.

            transform = transform.rotate_deg(rotate)
            rgbFace_alt = self._get_rgb_face(alt=True)

            renderer.draw_markers(gc, right, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, left, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_thin_diamond(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = renderer.points_to_pixels(self._markersize)
        transform = Affine2D().translate(-0.5, -0.5) \
            .rotate_deg(45)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()
        if fs=='full':
            transform = transform.scale(offset * 0.6, offset)
            renderer.draw_markers(gc, Path.unit_rectangle(), transform,
                                  path, path_trans, rgbFace)
        else:
            right = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
            left = 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.

            # scale after rotation
            transform = transform.rotate_deg(rotate).scale(offset * 0.6, offset)
            rgbFace_alt = self._get_rgb_face(alt=True)

            renderer.draw_markers(gc, right, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, left, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_pentagon(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5 * renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()

        polypath = Path.unit_regular_polygon(5)

        if fs == 'full':
            renderer.draw_markers(gc, polypath, transform,
                                  path, path_trans, rgbFace)
        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

            rgbFace_alt = self._get_rgb_face(alt=True)
            renderer.draw_markers(gc, mpath, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, mpath_alt, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_star(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5 * renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()

        polypath = Path.unit_regular_star(5, innerCircle=0.381966)

        if fs == 'full':
            renderer.draw_markers(gc, polypath, transform,
                                  path, path_trans, rgbFace)
        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

            rgbFace_alt = self._get_rgb_face(alt=True)
            renderer.draw_markers(gc, mpath, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, mpath_alt, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_hexagon1(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5 * renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()

        polypath = Path.unit_regular_polygon(6)

        if fs == 'full':
            renderer.draw_markers(gc, polypath, transform,
                                  path, path_trans, rgbFace)
        else:
            verts = polypath.vertices

            # not drawing inside lines
            x = 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

            rgbFace_alt = self._get_rgb_face(alt=True)
            renderer.draw_markers(gc, mpath, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, mpath_alt, transform,
                                  path, path_trans, rgbFace_alt)


    def _draw_hexagon2(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5 * renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(30)

        rgbFace = self._get_rgb_face()
        fs = self.get_fillstyle()

        polypath = Path.unit_regular_polygon(6)

        if fs == 'full':
            renderer.draw_markers(gc, polypath, transform,
                                  path, path_trans, rgbFace)
        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

            rgbFace_alt = self._get_rgb_face(alt=True)
            renderer.draw_markers(gc, mpath, transform,
                                  path, path_trans, rgbFace)
            renderer.draw_markers(gc, mpath_alt, transform,
                                  path, path_trans, rgbFace_alt)


    _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]])
    def _draw_vline(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)
        renderer.draw_markers(gc, self._line_marker_path, transform,
                              path, path_trans)


    def _draw_hline(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(90)
        renderer.draw_markers(gc, self._line_marker_path, transform,
                              path, path_trans)


    _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]])
    def _draw_tickleft(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = renderer.points_to_pixels(self._markersize)
        marker_transform = Affine2D().scale(-offset, 1.0)
        renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
                              path, path_trans)


    def _draw_tickright(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = renderer.points_to_pixels(self._markersize)
        marker_transform = Affine2D().scale(offset, 1.0)
        renderer.draw_markers(gc, self._tickhoriz_path, marker_transform,
                              path, path_trans)


    _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]])
    def _draw_tickup(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = renderer.points_to_pixels(self._markersize)
        marker_transform = Affine2D().scale(1.0, offset)
        renderer.draw_markers(gc, self._tickvert_path, marker_transform,
                              path, path_trans)


    def _draw_tickdown(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 1.0)
        offset = renderer.points_to_pixels(self._markersize)
        marker_transform = Affine2D().scale(1.0, -offset)
        renderer.draw_markers(gc, self._tickvert_path, marker_transform,
                              path, path_trans)


    _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 _draw_plus(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)
        renderer.draw_markers(gc, self._plus_path, transform,
                              path, path_trans)


    _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 _draw_tri_down(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)
        renderer.draw_markers(gc, self._tri_path, transform,
                              path, path_trans)


    def _draw_tri_up(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(180)
        renderer.draw_markers(gc, self._tri_path, transform,
                              path, path_trans)


    def _draw_tri_left(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(90)
        renderer.draw_markers(gc, self._tri_path, transform,
                              path, path_trans)


    def _draw_tri_right(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(270)
        renderer.draw_markers(gc, self._tri_path, transform,
                              path, path_trans)


    _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]])
    def _draw_caretdown(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)
        renderer.draw_markers(gc, self._caret_path, transform,
                              path, path_trans)


    def _draw_caretup(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(180)
        renderer.draw_markers(gc, self._caret_path, transform,
                              path, path_trans)


    def _draw_caretleft(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(270)
        renderer.draw_markers(gc, self._caret_path, transform,
                              path, path_trans)


    def _draw_caretright(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset).rotate_deg(90)
        renderer.draw_markers(gc, self._caret_path, transform,
                              path, path_trans)


    _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 _draw_x(self, renderer, gc, path, path_trans):
        gc.set_snap(renderer.points_to_pixels(self._markersize) >= 3.0)
        offset = 0.5*renderer.points_to_pixels(self._markersize)
        transform = Affine2D().scale(offset)
        renderer.draw_markers(gc, self._x_path, transform,
                              path, path_trans)


    def update_from(self, other):
        'copy properties from other to self'
        Artist.update_from(self, other)
        self._linestyle = other._linestyle
        self._linewidth = other._linewidth
        self._color = other._color
        self._markersize = other._markersize
        self._markerfacecolor = other._markerfacecolor
        self._markerfacecoloralt = other._markerfacecoloralt
        self._markeredgecolor = other._markeredgecolor
        self._markeredgewidth = other._markeredgewidth
        self._fillstyle = other._fillstyle
        self._dashSeq = other._dashSeq
        self._dashcapstyle = other._dashcapstyle
        self._dashjoinstyle = other._dashjoinstyle
        self._solidcapstyle = other._solidcapstyle
        self._solidjoinstyle = other._solidjoinstyle

        self._linestyle = other._linestyle
        self._marker = other._marker
        self._markerFunc = other._markerFunc
        self._drawstyle = other._drawstyle


    def _get_rgb_face(self, alt=False):
        facecolor = self._get_markerfacecolor(alt=alt)
        if is_string_like(facecolor) and facecolor.lower()=='none':
            rgbFace = None
        else:
            rgbFace = colorConverter.to_rgb(facecolor)
        return rgbFace

    # some aliases....
    def set_aa(self, val):
        'alias for set_antialiased'
        self.set_antialiased(val)

    def set_c(self, val):
        'alias for set_color'
        self.set_color(val)


    def set_ls(self, val):
        'alias for set_linestyle'
        self.set_linestyle(val)


    def set_lw(self, val):
        'alias for set_linewidth'
        self.set_linewidth(val)


    def set_mec(self, val):
        'alias for set_markeredgecolor'
        self.set_markeredgecolor(val)


    def set_mew(self, val):
        'alias for set_markeredgewidth'
        self.set_markeredgewidth(val)


    def set_mfc(self, val):
        'alias for set_markerfacecolor'
        self.set_markerfacecolor(val)

    def set_mfcalt(self, val):
        'alias for set_markerfacecoloralt'
        self.set_markerfacecoloralt(val)

    def set_ms(self, val):
        'alias for set_markersize'
        self.set_markersize(val)

    def get_aa(self):
        'alias for get_antialiased'
        return self.get_antialiased()

    def get_c(self):
        'alias for get_color'
        return self.get_color()


    def get_ls(self):
        'alias for get_linestyle'
        return self.get_linestyle()


    def get_lw(self):
        'alias for get_linewidth'
        return self.get_linewidth()


    def get_mec(self):
        'alias for get_markeredgecolor'
        return self.get_markeredgecolor()


    def get_mew(self):
        'alias for get_markeredgewidth'
        return self.get_markeredgewidth()


    def get_mfc(self):
        'alias for get_markerfacecolor'
        return self.get_markerfacecolor()

    def get_mfcalt(self, alt=False):
        'alias for get_markerfacecoloralt'
        return self.get_markerfacecoloralt()

    def get_ms(self):
        'alias for get_markersize'
        return self.get_markersize()

    def set_dash_joinstyle(self, s):
        """
        Set the join style for dashed linestyles
        ACCEPTS: ['miter' | 'round' | 'bevel']
        """
        s = s.lower()
        if s not in self.validJoin:
            raise ValueError('set_dash_joinstyle passed "%s";\n' % (s,)
                  + 'valid joinstyles are %s' % (self.validJoin,))
        self._dashjoinstyle = s

    def set_solid_joinstyle(self, s):
        """
        Set the join style for solid linestyles
        ACCEPTS: ['miter' | 'round' | 'bevel']
        """
        s = s.lower()
        if s not in self.validJoin:
            raise ValueError('set_solid_joinstyle passed "%s";\n' % (s,)
                  + 'valid joinstyles are %s' % (self.validJoin,))
        self._solidjoinstyle = s


    def get_dash_joinstyle(self):
        """
        Get the join style for dashed linestyles
        """
        return self._dashjoinstyle

    def get_solid_joinstyle(self):
        """
        Get the join style for solid linestyles
        """
        return self._solidjoinstyle

    def set_dash_capstyle(self, s):
        """
        Set the cap style for dashed linestyles

        ACCEPTS: ['butt' | 'round' | 'projecting']
        """
        s = s.lower()
        if s not in self.validCap:
            raise ValueError('set_dash_capstyle passed "%s";\n' % (s,)
                  + 'valid capstyles are %s' % (self.validCap,))

        self._dashcapstyle = s


    def set_solid_capstyle(self, s):
        """
        Set the cap style for solid linestyles

        ACCEPTS: ['butt' | 'round' |  'projecting']
        """
        s = s.lower()
        if s not in self.validCap:
            raise ValueError('set_solid_capstyle passed "%s";\n' % (s,)
                  + 'valid capstyles are %s' % (self.validCap,))

        self._solidcapstyle = s


    def get_dash_capstyle(self):
        """
        Get the cap style for dashed linestyles
        """
        return self._dashcapstyle


    def get_solid_capstyle(self):
        """
        Get the cap style for solid linestyles
        """
        return self._solidcapstyle


    def is_dashed(self):
        'return True if line is dashstyle'
        return self._linestyle in ('--', '-.', ':')

class VertexSelector:
    """
    Manage the callbacks to maintain a list of selected vertices for
    :class:`matplotlib.lines.Line2D`. Derived classes should override
    :meth:`~matplotlib.lines.VertexSelector.process_selected` to do
    something with the picks.

    Here is an example which highlights the selected verts with red
    circles::

        import numpy as np
        import matplotlib.pyplot as plt
        import matplotlib.lines as lines

        class HighlightSelected(lines.VertexSelector):
            def __init__(self, line, fmt='ro', **kwargs):
                lines.VertexSelector.__init__(self, line)
                self.markers, = self.axes.plot([], [], fmt, **kwargs)

            def process_selected(self, ind, xs, ys):
                self.markers.set_data(xs, ys)
                self.canvas.draw()

        fig = plt.figure()
        ax = fig.add_subplot(111)
        x, y = np.random.rand(2, 30)
        line, = ax.plot(x, y, 'bs-', picker=5)

        selector = HighlightSelected(line)
        plt.show()

    """
    def __init__(self, line):
        """
        Initialize the class with a :class:`matplotlib.lines.Line2D`
        instance.  The line should already be added to some
        :class:`matplotlib.axes.Axes` instance and should have the
        picker property set.
        """
        if not hasattr(line, 'axes'):
            raise RuntimeError('You must first add the line to the Axes')

        if line.get_picker() is None:
            raise RuntimeError('You must first set the picker property of the line')

        self.axes = line.axes
        self.line = line
        self.canvas = self.axes.figure.canvas
        self.cid = self.canvas.mpl_connect('pick_event', self.onpick)

        self.ind = set()


    def process_selected(self, ind, xs, ys):
        """
        Default "do nothing" implementation of the
        :meth:`process_selected` method.

        *ind* are the indices of the selected vertices.  *xs* and *ys*
        are the coordinates of the selected vertices.
        """
        pass

    def onpick(self, event):
        'When the line is picked, update the set of selected indicies.'
        if event.artist is not self.line: return

        for i in event.ind:
            if i in self.ind:
                self.ind.remove(i)
            else:
                self.ind.add(i)


        ind = list(self.ind)
        ind.sort()
        xdata, ydata = self.line.get_data()
        self.process_selected(ind, xdata[ind], ydata[ind])

lineStyles = Line2D._lineStyles
lineMarkers = Line2D._markers
drawStyles = Line2D.drawStyles
fillStyles = Line2D.fillStyles

docstring.interpd.update(Line2D = artist.kwdoc(Line2D))

# You can not set the docstring of an instancemethod,
# but you can on the underlying function.  Go figure.
docstring.dedent_interpd(Line2D.__init__.im_func)
