1815 lines
63 KiB
Python
Executable File
1815 lines
63 KiB
Python
Executable File
"""
|
|
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
|
|
| '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]])
|
|
# 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]])
|
|
_triangle_path_d = Path([[-3/5., -1/5.], [3/5., -1/5.], [1.0, -1.0], [-1.0, -1.0], [-3/5., -1/5.]])
|
|
_triangle_path_l = Path([[0.0, 1.0], [0.0, -1.0], [-1.0, -1.0], [0.0, 1.0]])
|
|
_triangle_path_r = Path([[0.0, 1.0], [0.0, -1.0], [1.0, -1.0], [0.0, 1.0]])
|
|
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)
|