Logo Search packages:      
Sourcecode: matplotlib version File versions


from __future__ import division
import math
from matplotlib import rcParams
from numerix import array, arange, sin, cos, pi, Float, sqrt, \
     matrixmultiply, sqrt, nonzero, equal, asarray, dot, concatenate
from artist import Artist, setp, kwdocd
from cbook import enumerate, dedent
from colors import colorConverter
from lines import Line2D
from transforms import bound_vertices
import matplotlib.nxutils as nxutils

from numerix.mlab import amin
from mlab import dist_point_to_segment

import artist

# these are not available for the object inspector until after the
# class is build so we define an initial set here for the init
# function and they will be overridden after object defn
kwdocd['Patch'] = """\
          alpha: float
          animated: [True | False]
          antialiased or aa: [True | False]
          clip_box: a matplotlib.transform.Bbox instance
          clip_on: [True | False]
          edgecolor or ec: any matplotlib color
          facecolor or fc: any matplotlib color
          figure: a matplotlib.figure.Figure instance
          fill: [True | False]
          hatch: unknown
          label: any string
          linewidth or lw: float
          lod: [True | False]
          transform: a matplotlib.transform transformation instance
          visible: [True | False]
          zorder: any number

00042 class Patch(Artist):
    A patch is a 2D thingy with a face color and an edge color

    If any of edgecolor, facecolor, linewidth, or antialiased are
    None, they default to their rc params setting

    zorder = 1
00051     def __init__(self,
                 antialiased = None,
                 hatch = None,
        The following kwarg properties are supported

        if edgecolor is None: edgecolor = rcParams['patch.edgecolor']
        if facecolor is None: facecolor = rcParams['patch.facecolor']
        if linewidth is None: linewidth = rcParams['patch.linewidth']
        if antialiased is None: antialiased = rcParams['patch.antialiased']

        self._edgecolor = edgecolor
        self._facecolor = facecolor
        self._linewidth = linewidth
        self._antialiased = antialiased
        self._hatch = hatch
        self.fill = fill

        if len(kwargs): setp(self, **kwargs)
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00081     def pick(self, mouseevent):
        if the mouse click is inside the vertices defining the patch, fire off a backend_bases.PickEvent
        if not self.pickable(): return
        picker = self.get_picker()
        if callable(picker):
            hit, props = picker(self, mouseevent)
            if hit:
                self.figure.canvas.pick_event(mouseevent, self, **props)
        elif picker:
            x, y = mouseevent.xdata, mouseevent.ydata
            if x is not None and y is not None:
                xyverts = self.get_verts()
                inside = nxutils.pnpoly(x, y, xyverts)
                if inside:
                    self.figure.canvas.pick_event(mouseevent, self)

    def update_from(self, other):
        Artist.update_from(self, other)

    def get_antialiased(self):
        return self._antialiased

    def get_edgecolor(self):
        return self._edgecolor

    def get_facecolor(self):
        return self._facecolor

    def get_linewidth(self):
        return self._linewidth

00124     def set_antialiased(self, aa):
        Set whether to use antialiased rendering

        ACCEPTS: [True | False]
        self._antialiased = aa

00132     def set_edgecolor(self, color):
        Set the patch edge color

        ACCEPTS: any matplotlib color
        self._edgecolor = color

00140     def set_facecolor(self, color):
        Set the patch face color

        ACCEPTS: any matplotlib color
        self._facecolor = color

00148     def set_linewidth(self, w):
        Set the patch linewidth in points

        ACCEPTS: float
        self._linewidth = w

00156     def set_fill(self, b):
        Set whether to fill the patch

        ACCEPTS: [True | False]
        self.fill = b

    def get_fill(self):
        'return whether fill is set'
        return self.fill

00168     def set_hatch(self, h):
        Set the hatching pattern

        hatch can be one of:
        /   - diagonal hatching
        \   - back diagonal
        |   - vertical
        -   - horizontal
        #   - crossed
        x   - crossed diagonal
        letters can be combined, in which case all the specified
        hatchings are done
        if same letter repeats, it increases the density of hatching
        in that direction

        1. Hatching is supported in the PostScript
        backend only.

        2. Hatching is done with solid black lines of width 0.
        self._hatch = h

    def get_hatch(self):
        'return the current hatching pattern'
        return self._hatch

    def draw(self, renderer):
        if not self.get_visible(): return
        gc = renderer.new_gc()

        if not self.fill or self._facecolor is None: rgbFace = None
        else: rgbFace = colorConverter.to_rgb(self._facecolor)

        if self._hatch:
            gc.set_hatch(self._hatch )

        verts = self.get_verts()
        tverts = self.get_transform().seq_xy_tups(verts)

        renderer.draw_polygon(gc, rgbFace, tverts)


00222     def get_verts(self):
        Return the vertices of the patch
        raise NotImplementedError('Derived must override')

    def get_window_extent(self, renderer=None):
        verts = self.get_verts()
        tverts = self.get_transform().seq_xy_tups(verts)
        return bound_vertices(tverts)

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

    def set_ec(self, val):
        'alias for set_edgecolor'

    def set_fc(self, val):
        'alias for set_facecolor'

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

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

    def get_ec(self):
        'alias for get_edgecolor'
        return self.get_edgecolor()

    def get_fc(self):
        'alias for get_facecolor'
        return self.get_facecolor()

class Shadow(Patch):
    def __init__(self, patch, ox, oy, props=None, **kwargs):
        Create a shadow of the patch offset by ox, oy.  props, if not None is
        a patch property update dictionary.  If None, the shadow will have
        have the same color as the face, but darkened

        kwargs are
        self.ox, self.oy = ox, oy
        self.patch = patch
        self.props = props
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

    def _update(self):
        if self.props is not None:
            r,g,b,a = colorConverter.to_rgba(self.patch.get_facecolor())
            rho = 0.3
            r = rho*r
            g = rho*g
            b = rho*b


    def get_verts(self):
        verts = self.patch.get_verts()
        xs = self.convert_xunits([x+self.ox for x,y in verts])
        ys = self.convert_yunits([y+self.oy for x,y in verts])
        return zip(xs, ys)

    def _draw(self, renderer):
        'draw the shadow'
        Patch.draw(self, renderer)

00314 class Rectangle(Patch):
    Draw a rectangle with lower left at xy=(x,y) with specified
    width and height


00321     def __init__(self, xy, width, height,
        xy is an x,y tuple lower, left

        width and height are width and height of rectangle

        fill is a boolean indicating whether to fill the rectangle

        Valid kwargs are:

        Patch.__init__(self, **kwargs)

        self.xy  = list(xy)
        self.width, self.height = width, height
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00341     def get_verts(self):
        Return the vertices of the rectangle
        x, y = self.xy

        left, right = self.convert_xunits((x, x + self.width))
        bottom, top = self.convert_yunits((y, y + self.height))

        return ( (left, bottom), (left, top),
                 (right, top), (right, bottom),

    def get_x(self):
        "Return the left coord of the rectangle"
        return self.xy[0]

    def get_y(self):
        "Return the bottom coord of the rectangle"
        return self.xy[1]

    def get_width(self):
        "Return the width of the  rectangle"
        return self.width

    def get_height(self):
        "Return the height of the rectangle"
        return self.height

00370     def set_x(self, x):
        Set the left coord of the rectangle

        ACCEPTS: float
        self.xy[0] = x

00378     def set_y(self, y):
        Set the bottom coord of the rectangle

        ACCEPTS: float
        self.xy[1] = y

00386     def set_width(self, w):
        Set the width rectangle

        ACCEPTS: float
        self.width = w

00394     def set_height(self, h):
        Set the width rectangle

        ACCEPTS: float
        self.height = h

00402     def set_bounds(self, *args):
        Set the bounds of the rectangle: l,b,w,h

        ACCEPTS: (left, bottom, width, height)
        if len(args)==0:
            l,b,w,h = args[0]
            l,b,w,h = args
        self.xy = [l,b]
        self.width = w
        self.height = h

00417 class RegularPolygon(Patch):
    A regular polygon patch.
00421     def __init__(self, xy, numVertices, radius=5, orientation=0,
        xy is a length 2 tuple (the center)
        numVertices is the number of vertices.
        radius is the distance from the center to each of the vertices.
        orientation is in radians and rotates the polygon.

        Valid kwargs are:
        Patch.__init__(self, **kwargs)

        self.xy = xy
        self.numVertices = numVertices
        self.radius = radius
        self.orientation = orientation

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

00443     def get_verts(self):
        theta = 2*pi/self.numVertices*arange(self.numVertices) + \
        r = float(self.radius)
        x, y = map(float, self.xy)

        xs = x + r*cos(theta)
        ys = y + r*sin(theta)

        #xs = self.convert_xunits(xs)
        #ys = self.convert_yunits(ys)

        self.verts = zip(xs, ys)

        return self.verts

00460 class Polygon(Patch):
    A general polygon patch.
00464     def __init__(self, xy, **kwargs):
        xy is a sequence of (x,y) 2 tuples

        Valid kwargs are:
        See Patch documentation for additional kwargs

        Patch.__init__(self, **kwargs)
        if not isinstance(xy, list):
            xy = list(xy)
        self.xy = xy
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00481     def get_verts(self):
        xs, ys = zip(*self.xy)[:2]
        xs = self.convert_xunits(xs)
        ys = self.convert_yunits(ys)
        return zip(xs, ys)

class Wedge(Polygon):
    def __init__(self, center, r, theta1, theta2,
                 dtheta=0.1, **kwargs):
        Draw a wedge centered at x,y tuple center with radius r that
        sweeps theta1 to theta2 (angles)

        dtheta is the resolution in degrees

        Valid kwargs are:

        xc, yc = center
        rads = (math.pi/180.)*arange(theta1, theta2+0.1*dtheta, dtheta)
        xs = r*cos(rads)+xc
        ys = r*sin(rads)+yc
        verts = [center]
        verts.extend([(x,y) for x,y in zip(xs,ys)])

        Polygon.__init__(self, verts, **kwargs)
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00511 class Arrow(Polygon):
    An arrow patch
00515     def __init__( self, x, y, dx, dy, width=1.0, **kwargs ):
        """Draws an arrow, starting at (x,y), direction and length
        given by (dx,dy) the width of the arrow is scaled by width

        Valid kwargs are:
        arrow = array( [
            [ 0.0,  0.1 ], [ 0.0, -0.1],
            [ 0.8, -0.1 ], [ 0.8, -0.3],
            [ 1.0,  0.0 ], [ 0.8,  0.3],
            [ 0.8,  0.1 ] ] )
        L = sqrt(dx**2+dy**2) or 1 # account for div by zero
        arrow[:,0] *= L
        arrow[:,1] *= width
        cx = float(dx)/L
        sx = float(dy)/L
        M = array( [ [ cx, sx],[ -sx, cx ] ] )
        verts = matrixmultiply( arrow, M )+ [x,y]
        Polygon.__init__( self, [ tuple(t) for t in verts ], **kwargs )
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00537 class FancyArrow(Polygon):
    """Like Arrow, but lets you set head width and head height independently."""

00540     def __init__(self, x, y, dx, dy, width=0.001, length_includes_head=False, \
        head_width=None, head_length=None, shape='full', overhang=0, \
        """Returns a new Arrow.

        length_includes_head: True if head is counted in calculating the length.

        shape: ['full', 'left', 'right']

        overhang: distance that the arrow is swept back (0 overhang means
        triangular shape).

        head_starts_at_zero: if True, the head starts being drawn at coordinate
        0 instead of ending at coordinate 0.

        Valid kwargs are:

        if head_width is None:
            head_width = 3 * width
        if head_length is None:
            head_length = 1.5 * head_width

        distance = sqrt(dx**2 + dy**2)
        if length_includes_head:
        if not length:
            verts = [] #display nothing if empty
            #start by drawing horizontal arrow, point at (0,0)
            hw, hl, hs, lw = head_width, head_length, overhang, width
            left_half_arrow = array([
                [0.0,0.0],                  #tip
                [-hl, -hw/2.0],             #leftmost
                [-hl*(1-hs), -lw/2.0], #meets stem
                [-length, -lw/2.0],          #bottom left
                [-length, 0],
            #if we're not including the head, shift up by head length
            if not length_includes_head:
                left_half_arrow += [head_length, 0]
            #if the head starts at 0, shift up by another head length
            if head_starts_at_zero:
                left_half_arrow += [head_length/2.0, 0]
            #figure out the shape, and complete accordingly
            if shape == 'left':
                coords = left_half_arrow
                right_half_arrow = left_half_arrow*[1,-1]
                if shape == 'right':
                    coords = right_half_arrow
                elif shape == 'full':
                    raise ValueError, "Got unknown shape: %s" % shape
            cx = float(dx)/distance
            sx = float(dy)/distance
            M = array([[cx, sx],[-sx,cx]])
            verts = matrixmultiply(coords, M) + (x+dx, y+dy)

        Polygon.__init__(self, map(tuple, verts), **kwargs)
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00606 class YAArrow(Polygon):
    Yet another arrow class

    This is an arrow that is defined in display space and has a tip at
    x1,y1 and a base at x2, y2.
00613     def __init__(self, dpi, xytip, xybase, width=4, frac=0.1, headwidth=12, **kwargs):
        xytip : (x,y) location of arrow tip
        xybase : (x,y) location the arrow base mid point
        dpi : the figure dpi instance (fig.dpi)
        width : the width of the arrow in points
        frac  : the fraction of the arrow length occupied by the head
        headwidth : the width of the base of the arrow head in points

        Valid kwargs are:

        self.dpi = dpi
        self.xytip = xytip
        self.xybase = xybase
        self.width = width
        self.frac = frac
        self.headwidth = headwidth
        verts = self.get_verts()
        Polygon.__init__(self, verts, **kwargs)
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00638     def get_verts(self):
        # the base vertices
        x1, y1 = self.xytip
        x2, y2 = self.xybase
        k1 = self.width*self.dpi.get()/72./2.
        k2 = self.headwidth*self.dpi.get()/72./2.
        xb1, yb1, xb2, yb2 = self.getpoints(x1, y1, x2, y2, k1)

        # a point on the segment 20% of the distance from the tip to the base
        theta = math.atan2(y2-y1, x2-x1)
        r = math.sqrt((y2-y1)**2. + (x2-x1)*2.)
        xm = x1 + self.frac * r * math.cos(theta)
        ym = y1 + self.frac * r * math.sin(theta)
        xc1, yc1, xc2, yc2 = self.getpoints(x1, y1, xm, ym, k1)
        xd1, yd1, xd2, yd2 = self.getpoints(x1, y1, xm, ym, k2)

        xs = self.convert_xunits([xb1, xb2, xc2, xd2, x1, xd1, xc1])
        ys = self.convert_yunits([yb1, yb2, yc2, yd2, y1, yd1, yc1])
        return zip(xs, ys)

00660     def getpoints(self, x1,y1,x2,y2, k):
        for line segment defined by x1,y1 and x2,y2, return the points on
        the line that is perpendicular to the line and intersects x2,y2
        and the distance from x2,y2 ot the returned points is k
        x1,y1,x2,y2,k = map(float, (x1,y1,x2,y2,k))
        m = (y2-y1)/(x2-x1)
        pm = -1./m
        a = 1
        b = -2*y2
        c = y2**2. - k**2.*pm**2./(1. + pm**2.)

        y3a = (-b + math.sqrt(b**2.-4*a*c))/(2.*a)
        x3a = (y3a - y2)/pm + x2

        y3b = (-b - math.sqrt(b**2.-4*a*c))/(2.*a)
        x3b = (y3b - y2)/pm + x2
        return x3a, y3a, x3b, y3b

00681 class CirclePolygon(RegularPolygon):
    A circle patch
00685     def __init__(self, xy, radius=5,
                 resolution=20,  # the number of vertices
        Create a circle at xy=(x,y) with radius given by 'radius'

        Valid kwargs are:

        self.center = xy
        self.radius = radius
        RegularPolygon.__init__(self, xy,
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00705 class Ellipse(Patch):
    A scale-free ellipse
00709     def __init__(self, xy, width, height, angle=0.0, **kwargs):
        xy - center of ellipse
        width - length of horizontal axis
        height - length of vertical axis
        angle - rotation in degrees (anti-clockwise)

        Valid kwargs are:
        Patch.__init__(self, **kwargs)

        # self.center  = array(xy, Float)
        self.center = xy
        self.width, self.height = width, height
        self.angle = angle

00726     def get_verts(self):
        x,y = self.center
        l,r = x-self.width/2.0, x+self.width/2.0
        b,t = y-self.height/2.0, y+self.height/2.0
        x,l,r = self.convert_xunits((x,l,r))
        y,b,t = self.convert_yunits((y,b,t))
        return array(((x,y),(l,y),(x,t),(r,y),(x,b)), Float)

    def draw(self, renderer):
        if not self.get_visible(): return
        gc = renderer.new_gc()


        if not self.fill or self._facecolor is None: rgbFace = None
        else: rgbFace = colorConverter.to_rgb(self._facecolor)

        if self._hatch:
            gc.set_hatch(self._hatch )

        tverts = self.get_transform().seq_xy_tups(self.get_verts())
        # center is first vert
        width = tverts[3,0] - tverts[1,0]
        height = tverts[2,1] - tverts[4,1]

        renderer.draw_arc(gc, rgbFace, tverts[0,0], tverts[0,1],
                          width, height, 0.0, 360.0, self.angle)

00760 class Circle(Ellipse):
    A circle patch
00764     def __init__(self, xy, radius=5,
        Create true circle at center xy=(x,y) with given radius;
        unlike circle polygon which is a polygonal approcimation, this
        uses splines and is much closer to a scale free circle

        Valid kwargs are:

        if kwargs.has_key('resolution'):
            import warnings
            warnings.warn('Circle is now scale free.  Use CirclePolygon instead!', DeprecationWarning)

        self.radius = radius
        Ellipse.__init__(self, xy, radius*2, radius*2, **kwargs)
    __init__.__doc__ = dedent(__init__.__doc__) % kwdocd

00785 class PolygonInteractor:
    An polygon editor.


      't' toggle vertex markers on and off.  When vertex markers are on,
          you can move them, delete them

      'd' delete the vertex under point

      'i' insert a vertex at point.  You must be within epsilon of the
          line connecting two existing vertices


    showverts = True
    epsilon = 5  # max pixel distance to count as a vertex hit

    def __init__(self, poly):
        if poly.figure is None:
            raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
        canvas = poly.figure.canvas
        self.poly = poly
        self.poly.verts = list(self.poly.verts)
        x, y = zip(*self.poly.verts)
        self.line = Line2D(x,y,marker='o', markerfacecolor='r')

        cid = self.poly.add_callback(self.poly_changed)
        self._ind = None # the active vert

        canvas.mpl_connect('button_press_event', self.button_press_callback)
        canvas.mpl_connect('key_press_event', self.key_press_callback)
        canvas.mpl_connect('button_release_event', self.button_release_callback)
        canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
        self.canvas = canvas

    def poly_changed(self, poly):
        'this method is called whenever the polygon object is called'
        # only copy the artist props to the line (except visibility)
        vis = self.line.get_visible()
        Artist.update_from(self.line, poly)
        self.line.set_visible(vis)  # don't use the poly visibility state

    def get_ind_under_point(self, event):
        'get the index of the vertex under point if within epsilon tolerance'
        x, y = zip(*self.poly.verts)

        # display coords
        xt, yt = self.poly.get_transform().numerix_x_y(x, y)
        d = sqrt((xt-event.x)**2 + (yt-event.y)**2)
        indseq = nonzero(equal(d, amin(d)))
        ind = indseq[0]

        if d[ind]>=self.epsilon:
            ind = None

        return ind

    def button_press_callback(self, event):
        'whenever a mouse button is pressed'
        if not self.showverts: return
        if event.inaxes==None: return
        if event.button != 1: return
        self._ind = self.get_ind_under_point(event)

    def button_release_callback(self, event):
        'whenever a mouse button is released'
        if not self.showverts: return
        if event.button != 1: return
        self._ind = None

    def key_press_callback(self, event):
        'whenever a key is pressed'
        if not event.inaxes: return
        if event.key=='t':
            self.showverts = not self.showverts
            if not self.showverts: self._ind = None
        elif event.key=='d':
            ind = self.get_ind_under_point(event)
            if ind is not None:
                self.poly.verts = [tup for i,tup in enumerate(self.poly.verts) if i!=ind]
        elif event.key=='i':
            xys = self.poly.get_transform().seq_xy_tups(self.poly.verts)
            p = event.x, event.y # display coords
            for i in range(len(xys)-1):
                s0 = xys[i]
                s1 = xys[i+1]
                d = dist_point_to_segment(p, s0, s1)
                if d<=self.epsilon:
                    self.poly.verts.insert(i+1, (event.xdata, event.ydata))


    def motion_notify_callback(self, event):
        'on mouse movement'
        if not self.showverts: return
        if self._ind is None: return
        if event.inaxes is None: return
        if event.button != 1: return
        x,y = event.xdata, event.ydata
        self.poly.verts[self._ind] = x,y

def bbox_artist(artist, renderer, props=None, fill=True):
    This is a debug function to draw a rectangle around the bounding
    box returned by get_window_extent of an artist, to test whether
    the artist is returning the correct bbox

    props is a dict of rectangle props with the additional property
    'pad' that sets the padding around the bbox in points
    if props is None: props = {}
    props = props.copy() # don't want to alter the pad externally
    pad = props.pop('pad', 4)
    pad = renderer.points_to_pixels(pad)
    bbox = artist.get_window_extent(renderer)
    l,b,w,h = bbox.get_bounds()
    r = Rectangle(xy=(l,b),
    r.set_clip_on( False )

def draw_bbox(bbox, renderer, color='k', trans=None):
    This is a debug function to draw a rectangle around the bounding
    box returned by get_window_extent of an artist, to test whether
    the artist is returning the correct bbox

    l,b,w,h = bbox.get_bounds()
    r = Rectangle(xy=(l,b),
    if trans is not None: r.set_transform(trans)
    r.set_clip_on( False )

artist.kwdocd['Patch'] = patchdoc = artist.kwdoc(Patch)

for k in ('Rectangle', 'Circle', 'RegularPolygon', 'Polygon', 'Wedge', 'Arrow',
          'FancyArrow', 'YAArrow', 'CirclePolygon', 'Ellipse'):
    artist.kwdocd[k] = patchdoc

Generated by  Doxygen 1.6.0   Back to index