Revision: 3928
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3928&view=rev
Author:   mdboom
Date:     2007-10-08 11:10:11 -0700 (Mon, 08 Oct 2007)

Log Message:
-----------
More work on collections.

Modified Paths:
--------------
    branches/transforms/examples/collections_demo.py
    branches/transforms/lib/matplotlib/axes.py
    branches/transforms/lib/matplotlib/backends/backend_agg.py
    branches/transforms/lib/matplotlib/collections.py
    branches/transforms/lib/matplotlib/legend.py
    branches/transforms/lib/matplotlib/lines.py
    branches/transforms/lib/matplotlib/transforms.py
    branches/transforms/setupext.py
    branches/transforms/src/_backend_agg.cpp
    branches/transforms/src/_backend_agg.h

Modified: branches/transforms/examples/collections_demo.py
===================================================================
--- branches/transforms/examples/collections_demo.py    2007-10-08 12:45:23 UTC 
(rev 3927)
+++ branches/transforms/examples/collections_demo.py    2007-10-08 18:10:11 UTC 
(rev 3928)
@@ -45,6 +45,8 @@
 a = fig.add_subplot(2,2,1)
 col = collections.LineCollection([spiral], offsets=xyo,
                                 transOffset=a.transData)
+trans = transforms.Affine2D().scale(fig.dpi/72.0)
+col.set_transform(trans)  # the points to pixels transform
     # Note: the first argument to the collection initializer
     # must be a list of sequences of x,y tuples; we have only
     # one sequence, but we still have to put it in a list.
@@ -59,9 +61,6 @@
 
 # Make a transform for the line segments such that their size is
 # given in points:
-trans = transforms.scale_transform(fig.dpi/transforms.Value(72.),
-                                    fig.dpi/transforms.Value(72.))
-col.set_transform(trans)  # the points to pixels transform
 col.set_color(colors)
 
 a.autoscale_view()  # See comment above, after a.add_collection.
@@ -74,28 +73,25 @@
 
 col = collections.PolyCollection([spiral], offsets=xyo,
                                 transOffset=a.transData)
-a.add_collection(col, autolim=True)
-trans = transforms.scale_transform(fig.dpi/transforms.Value(72.),
-                                    fig.dpi/transforms.Value(72.))
+trans = transforms.Affine2D().scale(fig.dpi/72.0)
 col.set_transform(trans)  # the points to pixels transform
+a.add_collection(col, autolim=True)
 col.set_color(colors)
 
 
 a.autoscale_view()
 a.set_title('PolyCollection using offsets')
 
-
 # 7-sided regular polygons
 
 a = fig.add_subplot(2,2,3)
 
 col = collections.RegularPolyCollection(fig.dpi, 7,
-                                        sizes = N.fabs(xx)*10, offsets=xyo,
+                                        sizes = N.fabs(xx) / 10.0, offsets=xyo,
                                         transOffset=a.transData)
-a.add_collection(col, autolim=True)
-trans = transforms.scale_transform(fig.dpi/transforms.Value(72.),
-                                    fig.dpi/transforms.Value(72.))
+trans = transforms.Affine2D().scale(fig.dpi/72.0)
 col.set_transform(trans)  # the points to pixels transform
+a.add_collection(col, autolim=True)
 col.set_color(colors)
 a.autoscale_view()
 a.set_title('RegularPolyCollection using offsets')

Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py  2007-10-08 12:45:23 UTC (rev 
3927)
+++ branches/transforms/lib/matplotlib/axes.py  2007-10-08 18:10:11 UTC (rev 
3928)
@@ -1046,7 +1046,7 @@
         self._set_artist_props(collection)
         collection.set_clip_path(self.axesPatch)
         if autolim:
-            self.update_datalim(collection.get_verts(self.transData))
+            self.update_datalim(collection.get_datalim(self.transData))
         collection._remove_method = lambda h: self.collections.remove(h)
 
     def add_line(self, line):
@@ -1105,6 +1105,9 @@
         # limits and set the bound to be the bounds of the xydata.
         # Otherwise, it will compute the bounds of it's current data
         # and the data in xydata
+        # MGDTODO: This isn't always the most efficient way to do this... in
+        # some cases things should use update_datalim_bounds
+        
         if not ma.isMaskedArray(xys):
             xys = npy.asarray(xys)
         self.update_datalim_numerix(xys[:, 0], xys[:, 1])
@@ -1119,6 +1122,10 @@
        self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits)
        self.ignore_existing_data_limits = False
 
+    def update_datalim_bounds(self, bounds):
+        # MGDTODO: Document me
+        self.dataLim.bounds = Bbox.union([self.dataLim, bounds]).bounds
+        
     def _get_verts_in_data_coords(self, trans, xys):
         if trans == self.transData:
             return xys
@@ -4006,8 +4013,8 @@
                shading='faceted  --> edgecolors=None
            edgecolors also can be any mpl color or sequence of colors.
 
-           Optional kwargs control the PatchCollection properties:
-        %(PatchCollection)s
+           Optional kwargs control the Collection properties:
+        %(Collection)s
         """
 
         if not self._hold: self.cla()
@@ -4439,7 +4446,7 @@
 
           * alpha=1.0 : the alpha blending value
 
-        Return value is a mcoll.PatchCollection
+        Return value is a mcoll.Collection
         object
 
         Grid Orientation
@@ -4627,7 +4634,7 @@
 
           * alpha=1.0 : the alpha blending value
 
-        Return value is a collections.PatchCollection
+        Return value is a collections.Collection
         object
 
         See pcolor for an explantion of the grid orientation and the

Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py  2007-10-08 
12:45:23 UTC (rev 3927)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py  2007-10-08 
18:10:11 UTC (rev 3928)
@@ -131,11 +131,15 @@
         self._renderer.draw_markers(gc, marker_path, marker_trans.frozen(), 
path, trans.frozen(), rgbFace)
 
     def draw_path_collection(self, master_transform, clipbox, clippath, 
clippath_trans,
-                             paths, transforms, facecolors, edgecolors, 
linewidths, linestyles, antialiaseds):
+                             paths, transforms, offsets, transOffset, 
facecolors, edgecolors,
+                             linewidths, linestyles, antialiaseds):
         assert master_transform.is_affine
+        if transOffset is not None:
+            transOffset = transOffset.frozen()
         self._renderer.draw_path_collection(
             master_transform.frozen(), clipbox, clippath, clippath_trans,
-            paths, transforms, facecolors, edgecolors, linewidths, linestyles, 
antialiaseds)
+            paths, transforms, offsets, transOffset, facecolors, edgecolors, 
linewidths,
+            linestyles, antialiaseds)
         
     def draw_mathtext(self, gc, x, y, s, prop, angle):
         """

Modified: branches/transforms/lib/matplotlib/collections.py
===================================================================
--- branches/transforms/lib/matplotlib/collections.py   2007-10-08 12:45:23 UTC 
(rev 3927)
+++ branches/transforms/lib/matplotlib/collections.py   2007-10-08 18:10:11 UTC 
(rev 3928)
@@ -19,67 +19,20 @@
 import matplotlib.nxutils as nxutils
 import matplotlib.path as path
 
-# MGDTODO: Now that everything draws through draw_path_collection,
-# perhaps more stuff could be moved into the Collection base class
-# here
+# MGDTODO: Move this stuff
+from matplotlib.backends._backend_agg import get_path_collection_extents, \
+    point_in_path_collection
 
-class Collection(artist.Artist):
+class Collection(artist.Artist, cm.ScalarMappable):
     """
+    Base class for Collections.  Must be subclassed to be usable.
+
     All properties in a collection must be sequences or scalars;
     if scalars, they will be converted to sequences.  The
     property of the ith element of the collection is the
 
       prop[i % len(props)].
 
-    """
-
-    def __init__(self):
-        artist.Artist.__init__(self)
-
-
-    def get_verts(self):
-        'return seq of (x,y) in collection'
-        raise NotImplementedError('Derived must override')
-
-    def _get_value(self, val):
-        try: return (float(val), )
-        except TypeError:
-            if cbook.iterable(val) and len(val):
-                try: float(val[0])
-                except TypeError: pass # raise below
-                else: return val
-
-        raise TypeError('val must be a float or nonzero sequence of floats')
-
-
-# these are not available for the object inspector until after the
-# class is built so we define an initial set here for the init
-# function and they will be overridden after object defn
-artist.kwdocd['PatchCollection'] = """\
-    Valid PatchCollection kwargs are:
-
-      edgecolors=None,
-      facecolors=None,
-      linewidths=None,
-      antialiaseds = None,
-      offsets = None,
-      transOffset = transforms.identity_transform(),
-      norm = None,  # optional for cm.ScalarMappable
-      cmap = None,  # ditto
-
-    offsets and transOffset are used to translate the patch after
-    rendering (default no offsets)
-
-    If any of edgecolors, facecolors, linewidths, antialiaseds are
-    None, they default to their patch.* rc params setting, in sequence
-    form.
-"""
-
-class PatchCollection(Collection, cm.ScalarMappable):
-    """
-    Base class for filled regions such as PolyCollection etc.
-    It must be subclassed to be usable.
-
     kwargs are:
 
           edgecolors=None,
@@ -103,30 +56,45 @@
     draw time a call to scalar mappable will be made to set the face
     colors.
     """
+    _offsets = npy.zeros((1, 2))
+    _transOffset = transforms.IdentityTransform()
+
+    _facecolors = [None]
+    _edgecolors = [None]
+    _lw = [1.0]
+    _ls = [None]
+    _aa = [True]
+    _pickradius = 5.0
+    _transforms = [None]
+    
     zorder = 1
     def __init__(self,
                  edgecolors=None,
                  facecolors=None,
                  linewidths=None,
+                 linestyles='solid',
                  antialiaseds = None,
                  offsets = None,
                  transOffset = None,
                  norm = None,  # optional for ScalarMappable
                  cmap = None,  # ditto
+                 pickradius = 5.0,
+                 **kwargs
                  ):
         """
         Create a PatchCollection
 
         %(PatchCollection)s
         """
-        Collection.__init__(self)
+        artist.Artist.__init__(self)
         cm.ScalarMappable.__init__(self, norm, cmap)
 
         if facecolors is None: facecolors = mpl.rcParams['patch.facecolor']
         if edgecolors is None: edgecolors = mpl.rcParams['patch.edgecolor']
         if linewidths is None: linewidths = (mpl.rcParams['patch.linewidth'],)
         if antialiaseds is None: antialiaseds = 
(mpl.rcParams['patch.antialiased'],)
-
+        self.set_linestyles(linestyles)
+        
         self._facecolors  = _colors.colorConverter.to_rgba_list(facecolors)
         if edgecolors == 'None':
             self._edgecolors = self._facecolors
@@ -135,13 +103,80 @@
             self._edgecolors = _colors.colorConverter.to_rgba_list(edgecolors)
         self._linewidths  = self._get_value(linewidths)
         self._antialiaseds = self._get_value(antialiaseds)
-        #self._offsets = offsets
-        self._offsets = offsets
-        self._transOffset = transOffset
-        self._verts = []
 
-    __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+        self._uniform_offsets = None
+        self._offsets = npy.zeros((1, 2))
+        if offsets is not None:
+            offsets = npy.asarray(offsets)
+            if len(offsets.shape) == 1:
+                offsets = offsets[npy.newaxis,:]  # Make it Nx2.
+            if transOffset is not None:
+                Affine2D = transforms.Affine2D
+                self._offsets = offsets
+                self._transOffset = transOffset
+            else:
+                self._uniform_offsets = offsets
 
+        self._pickradius = pickradius
+        self.update(kwargs)
+
+    def _get_value(self, val):
+        try: return (float(val), )
+        except TypeError:
+            if cbook.iterable(val) and len(val):
+                try: float(val[0])
+                except TypeError: pass # raise below
+                else: return val
+
+        raise TypeError('val must be a float or nonzero sequence of floats')
+        
+    def get_paths(self):
+        raise NotImplementedError
+
+    def get_transforms(self):
+        return self._transforms
+        
+    def get_datalim(self, transData):
+        result = transforms.Bbox.from_lbrt(*get_path_collection_extents(
+                self.get_transform().frozen(),
+                self.get_paths(),
+                self.get_transforms(),
+                self._offsets,
+                self._transOffset.frozen()))
+        result = result.transformed(transData.inverted())
+        return result
+
+    def draw(self, renderer):
+        if not self.get_visible(): return
+        renderer.open_group(self.__class__.__name__)
+        transform = self.get_transform()
+        transOffset = self._transOffset
+        offsets = self._offsets
+
+        # MGDTODO: Transform the paths (since we don't keep track of segments 
anymore
+        if self.have_units():
+            segments = []
+            for segment in self._segments:
+                xs, ys = zip(*segment)
+                xs = self.convert_xunits(xs)
+                ys = self.convert_yunits(ys)
+                segments.append(zip(xs, ys))
+#             if self._offsets is not None:
+#                 xs = self.convert_xunits(self._offsets[:0])
+#                 ys = self.convert_yunits(self._offsets[:1])
+#                 offsets = zip(xs, ys)
+                
+        self.update_scalarmappable()
+
+        #print 'calling renderer draw line collection'
+        clippath, clippath_trans = self.get_transformed_clip_path_and_affine()
+
+        renderer.draw_path_collection(
+            transform, self.clipbox, clippath, clippath_trans,
+            self.get_paths(), self.get_transforms(), offsets, transOffset, 
+            self._facecolors, self._edgecolors, self._lw, self._ls, self._aa)
+        renderer.close_group(self.__class__.__name__)
+
     def contains(self, mouseevent):
         """
         Test whether the mouse event occurred in the collection.
@@ -149,25 +184,13 @@
         Returns T/F, dict(ind=itemlist), where every item in itemlist contains 
the event.
         """
         if callable(self._contains): return self._contains(self,mouseevent)
-        # TODO: Consider doing the test in data coordinates
-        # Patch transforms the mouse into data coordinates and does the
-        # test for membership there.  This is more efficient though it
-        # may not match the visual appearance of the polygon on the
-        # screen.  Regardless, patch and patch collection should use
-        # the same algorithm.  Here's the code in patch:
-        #
-        #    x, y = self.get_transform().inverse_xy_tup((mouseevent.x, 
mouseevent.y))
-        #    xyverts = self.get_verts()
-        #    inside = nxutils.pnpoly(x, y, xyverts)
-        #
-        ind = []
-        x, y = mouseevent.x, mouseevent.y
-        for i, thispoly in enumerate(self.get_transformed_patches()):
-            inside = nxutils.pnpoly(x, y, thispoly)
-            if inside: ind.append(i)
+        ind = point_in_path_collection(
+            mouseevent.x, mouseevent.y, self._pickradius,
+            self.get_transform(), self._paths, self._transforms, self._offsets,
+            self._offsetTrans, self._facecolors)
         return len(ind)>0,dict(ind=ind)
 
-
+    # MGDTODO
     def get_transformed_patches(self):
         """
         get a sequence of the polygons in the collection in display 
(transformed) space
@@ -206,12 +229,9 @@
             data.append(tverts)
         return data
 
-    def get_transoffset(self):
-        if self._transOffset is None:
-            self._transOffset = transforms.identity_transform()
-        return self._transOffset
-
-
+    def set_pickradius(self,pickradius): self.pickradius = 5
+    def get_pickradius(self): return self.pickradius
+    
     def set_linewidth(self, lw):
         """
         Set the linewidth(s) for the collection.  lw can be a scalar or a
@@ -224,6 +244,36 @@
     def set_linewidths(self, lw):
         self.set_linewidth(lw)
 
+    def set_linestyles(self, ls):
+        """
+        Set the linestyles(s) for the collection.
+        ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' |  (offset, 
on-off-dash-seq) ]
+        """
+        try:
+            if cbook.is_string_like(ls):
+                dashes = [backend_bases.GraphicsContextBase.dashd[ls]]
+            elif cbook.iterable(ls):
+                try:
+                    dashes = []
+                    for x in ls:
+                        if cbook.is_string_like(x):
+                            
dashes.append(backend_bases.GraphicsContextBase.dashd[ls])
+                        elif cbook.iterator(x) and len(x) == 2:
+                            dashes.append(x)
+                        else:
+                            raise ValueError()
+                except ValueError:
+                    if len(ls)==2:
+                        dashes = ls
+                    else:
+                        raise ValueError()
+            else:
+                raise ValueError()
+        except ValueError:
+            raise ValueError('Do not know how to convert %s to dashes'%ls)
+
+        self._ls = dashes
+        
     def set_color(self, c):
         """
         Set both the edgecolor and the facecolor.
@@ -277,6 +327,15 @@
             if cbook.is_string_like(self._edgecolors) and self._edgecolors != 
'None':
                 self._edgecolors = [(r,g,b,alpha) for r,g,b,a in 
self._edgecolors]
 
+    def get_linewidth(self):
+        return self._lw
+
+    def get_linestyle(self):
+        return self._ls
+
+    def get_dashes(self):
+        return self._ls
+                
     def update_scalarmappable(self):
         """
         If the scalar mappable array is not none, update facecolors
@@ -289,7 +348,31 @@
         self._facecolors = self.to_rgba(self._A, self._alpha)
         #print self._facecolors
 
-class QuadMesh(PatchCollection):
+
+# these are not available for the object inspector until after the
+# class is built so we define an initial set here for the init
+# function and they will be overridden after object defn
+artist.kwdocd['Collection'] = """\
+    Valid Collection kwargs are:
+
+      edgecolors=None,
+      facecolors=None,
+      linewidths=None,
+      antialiaseds = None,
+      offsets = None,
+      transOffset = transforms.identity_transform(),
+      norm = None,  # optional for cm.ScalarMappable
+      cmap = None,  # ditto
+
+    offsets and transOffset are used to translate the patch after
+    rendering (default no offsets)
+
+    If any of edgecolors, facecolors, linewidths, antialiaseds are
+    None, they default to their patch.* rc params setting, in sequence
+    form.
+"""
+        
+class QuadMesh(Collection):
     """
     Class for the efficient drawing of a quadrilateral mesh.
     A quadrilateral mesh consists of a grid of vertices. The dimensions
@@ -315,7 +398,7 @@
     (0, 2) .. (0, meshWidth), (1, 0), (1, 1), and so on.
     """
     def __init__(self, meshWidth, meshHeight, coordinates, showedges):
-        PatchCollection.__init__(self)
+        Collection.__init__(self)
         self._meshWidth = meshWidth
         self._meshHeight = meshHeight
         self._coordinates = coordinates
@@ -329,7 +412,7 @@
         # when creating/changing              ****** Why not?  speed?
         if not self.get_visible(): return
         transform = self.get_transform()
-        transoffset = self.get_transoffset()
+        transoffset = self._transOffset
         transform.freeze()
         transoffset.freeze()
         #print 'QuadMesh draw'
@@ -342,63 +425,26 @@
         transform.thaw()
         transoffset.thaw()
 
-class PolyCollection(PatchCollection):
+class PolyCollection(Collection):
     def __init__(self, verts, **kwargs):
         """
         verts is a sequence of ( verts0, verts1, ...) where verts_i is
         a sequence of xy tuples of vertices, or an equivalent
         numpy array of shape (nv,2).
 
-        %(PatchCollection)s
+        %(Collection)s
         """
-        PatchCollection.__init__(self,**kwargs)
-        self._verts = verts
+        Collection.__init__(self,**kwargs)
+        self.set_verts(verts)
     __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
 
     def set_verts(self, verts):
         '''This allows one to delay initialization of the vertices.'''
-        self._verts = verts
+        self._paths = [path.Path(v, closed=True) for v in verts]
 
-    def draw(self, renderer):
-        if not self.get_visible(): return
-        renderer.open_group('polycollection')
-        transform = self.get_transform()
-        transoffset = self.get_transoffset()
-
-
-        transform.freeze()
-        transoffset.freeze()
-        self.update_scalarmappable()
-        if cbook.is_string_like(self._edgecolors) and self._edgecolors[:2] == 
'No':
-            self._linewidths = (0,)
-            #self._edgecolors = self._facecolors
-        renderer.draw_poly_collection(
-            self._verts, transform, self.clipbox,
-            self._facecolors, self._edgecolors,
-            self._linewidths, self._antialiaseds,
-            self._offsets,  transoffset)
-        transform.thaw()
-        transoffset.thaw()
-        renderer.close_group('polycollection')
-
-
-    def get_verts(self, dataTrans=None):
-        '''Return vertices in data coordinates.
-        The calculation is incomplete in general; it is based
-        on the vertices or the offsets, whichever is using
-        dataTrans as its transformation, so it does not take
-        into account the combined effect of segments and offsets.
-        '''
-        verts = []
-        if self._offsets is None:
-            for seg in self._verts:
-                verts.extend(seg)
-            return [tuple(xy) for xy in verts]
-        if self.get_transoffset() == dataTrans:
-            return [tuple(xy) for xy in self._offsets]
-        raise NotImplementedError('Vertices in data coordinates are 
calculated\n'
-                + 'with offsets only if _transOffset == dataTrans.')
-
+    def get_paths(self):
+        return self._paths
+        
 class BrokenBarHCollection(PolyCollection):
     """
     A colleciton of horizontal bars spanning yrange with a sequence of
@@ -409,7 +455,7 @@
         xranges : sequence of (xmin, xwidth)
         yrange  : ymin, ywidth
 
-        %(PatchCollection)s
+        %(Collection)s
         """
         ymin, ywidth = yrange
         ymax = ymin + ywidth
@@ -417,7 +463,7 @@
         PolyCollection.__init__(self, verts, **kwargs)
     __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
 
-class RegularPolyCollection(PatchCollection):
+class RegularPolyCollection(Collection):
     def __init__(self,
                  dpi,
                  numsides,
@@ -437,7 +483,7 @@
 
         * rotation is the rotation of the polygon in radians
 
-        %(PatchCollection)s
+        %(Collection)s
 
         Example: see examples/dynamic_collection.py for complete example
 
@@ -459,14 +505,18 @@
 
 
         """
-        PatchCollection.__init__(self,**kwargs)
+        Collection.__init__(self,**kwargs)
         self._sizes = sizes
         self._dpi = dpi
-        self.numsides = numsides
-        self.rotation = rotation
-        self._update_verts()
+        self._paths = [path.Path.unit_regular_polygon(numsides)]
+        self._transforms = [transforms.Affine2D().scale(x) for x in sizes]
+        
     __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
 
+    def get_paths(self):
+        return self._paths
+    
+    # MGDTODO
     def get_transformed_patches(self):
         # Shouldn't need all these calls to asarray;
         # the variables should be converted when stored.
@@ -485,62 +535,6 @@
         return polys
 
 
-    def _update_verts(self):
-        r = 1.0/math.sqrt(math.pi)  # unit area
-        theta = (2*math.pi/self.numsides)*npy.arange(self.numsides) + 
self.rotation
-        self._verts = zip( r*npy.sin(theta), r*npy.cos(theta) )
-
-    def draw(self, renderer):
-        if not self.get_visible(): return
-        renderer.open_group('regpolycollection')
-        transform = self.get_transform()
-        transoffset = self.get_transoffset()
-
-        transform.freeze()
-        transoffset.freeze()
-        self.update_scalarmappable()
-        self._update_verts()
-        scales = npy.sqrt(npy.asarray(self._sizes)*self._dpi.get()/72.0)
-
-
-        offsets = self._offsets
-        if self._offsets is not None:
-            xs, ys = zip(*offsets)
-            #print 'converting: units=%s, 
converter=%s'%(self.axes.xaxis.units, self.axes.xaxis.converter)
-            xs = self.convert_xunits(xs)
-            ys = self.convert_yunits(ys)
-            offsets = zip(xs, ys)
-        else:
-            offsets = None
-
-        #print 'drawing offsets', offsets
-        #print 'drawing verts', self._verts
-        #print 'drawing scales', scales
-        if cbook.is_string_like(self._edgecolors) and self._edgecolors[:2] == 
'No':
-            #self._edgecolors = self._facecolors
-            self._linewidths = (0,)
-        renderer.draw_regpoly_collection(
-            self.clipbox,
-            offsets, transoffset,
-            self._verts, scales,
-            self._facecolors, self._edgecolors,
-            self._linewidths, self._antialiaseds)
-
-        transform.thaw()
-        transoffset.thaw()
-        renderer.close_group('regpolycollection')
-
-
-    def get_verts(self, dataTrans=None):
-        '''Return vertices in data coordinates.
-        The calculation is incomplete; it uses only
-        the offsets, and only if _transOffset is dataTrans.
-        '''
-        if self.get_transoffset() == dataTrans:
-            return [tuple(xy) for xy in self._offsets]
-        raise NotImplementedError('Vertices in data coordinates are 
calculated\n'
-                + 'only with offsets and only if _transOffset == dataTrans.')
-
 class StarPolygonCollection(RegularPolyCollection):
     def __init__(self,
                  dpi,
@@ -561,7 +555,7 @@
 
         * rotation is the rotation of the polygon in radians
 
-        %(PatchCollection)s
+        %(Collection)s
         """
 
         RegularPolyCollection.__init__(self, dpi, numsides, rotation, sizes, 
**kwargs)
@@ -595,7 +589,7 @@
 
         * rotation is the rotation of the polygon in radians
 
-        %(PatchCollection)s
+        %(Collection)s
         """
 
         RegularPolyCollection.__init__(self, dpi, numsides, rotation, sizes, 
**kwargs)
@@ -621,9 +615,8 @@
                  colors       = None,
                  antialiaseds  = None,
                  linestyles = 'solid',
-                 # MGDTODO: Deal with offsets (mayb
-#                offsets = None,
-#                transOffset = None,#transforms.identity_transform(),
+                 offsets = None,
+                 transOffset = None,
                  norm = None,
                  cmap = None,
                  pickradius = 5,
@@ -668,168 +661,52 @@
         matrix _A is not None (ie a call to set_array has been made), at
         draw time a call to scalar mappable will be made to set the colors.
         """
-        Collection.__init__(self)
-        cm.ScalarMappable.__init__(self, norm, cmap)
+        if colors is None: colors = mpl.rcParams['lines.color']
+        if linewidths is None: linewidths = (mpl.rcParams['lines.linewidth'],)
+        if antialiaseds is None: antialiaseds = 
(mpl.rcParams['lines.antialiased'],)
+        self.set_linestyles(linestyles)
 
-        if linewidths is None   :
-            linewidths   = (mpl.rcParams['lines.linewidth'], )
-        if colors is None       :
-            colors       = (mpl.rcParams['lines.color'],)
-        if antialiaseds is None :
-            antialiaseds = (mpl.rcParams['lines.antialiased'], )
+        Collection.__init__(
+            self,
+            edgecolors=colors,
+            linewidths=linewidths,
+            linestyles=linestyles,
+            antialiaseds=antialiaseds,
+            offsets=offsets,
+            transOffset=transOffset,
+            norm=norm,
+            cmap=cmap,
+            pickradius=pickradius,
+            **kwargs)
 
-        self._colors = _colors.colorConverter.to_rgba_list(colors)
-        self._aa = self._get_value(antialiaseds)
-        self._lw = self._get_value(linewidths)
-        self.set_linestyles(linestyles)
-        self._uniform_offsets = None
-        # MGDTODO: Deal with offsets
-#         if offsets is not None:
-#             offsets = npy.asarray(offsets)
-#             if len(offsets.shape) == 1:
-#                 offsets = offsets[npy.newaxis,:]  # Make it Nx2.
-#         if transOffset is None:
-#             if offsets is not None:
-#                 self._uniform_offsets = offsets
-#                 offsets = None
-#             transOffset = transforms.IdentityTransform()
-#         self._offsets = offsets
-#         self._transOffset = transOffset
+        self._facecolors = [None]
         self.set_segments(segments)
-        self.pickradius = pickradius
-        self.update(kwargs)
 
-    def contains(self, mouseevent):
-        """
-        Test whether the mouse event occurred in the collection.
-
-        Returns T/F, dict(ind=itemlist), where every item in itemlist contains 
the event.
-        """
-        import matplotlib.lines as ML
-        if callable(self._contains): return self._contains(self,mouseevent)
-
-        # TODO: add offset processing; adjusting the mouse for each offset
-        # will be somewhat cheaper than adjusting the segments.
-        if self._offsets != None:
-            raise NotImplementedError, "LineCollection does not yet support 
picking with offsets"
-
-        mx,my = mouseevent.x,mouseevent.y
-        transform = self.get_transform()
-
-        # MGDTODO: Numpify
-        ind = []
-        for this in xrange(len(self._segments)):
-            xy = transform.transform_point(self._segments[this])
-            this_ind = ML.segment_hits(mx,my,xy[:,0],xy[:,1],self.pickradius)
-            ind.extend([(this,k) for k in this_ind])
-        return len(ind)>0,dict(ind=ind)
-
-    def set_pickradius(self,pickradius): self.pickradius = 5
-    def get_pickradius(self): return self.pickradius
-
-    # MGDTODO: Support offsets (maybe)
-#     def get_transoffset(self):
-#         if self._transOffset is None:
-#             self._transOffset = transforms.identity_transform()
-#         return self._transOffset
-
+    def get_paths(self):
+        return self._paths
+        
     def set_segments(self, segments):
         if segments is None: return
+        segments = [npy.asarray(seg, npy.float_) for seg in segments]
+        if self._uniform_offsets is not None:
+            segments = self._add_offsets(segments)
         self._paths = [path.Path(seg, closed=False) for seg in segments]
         
-#         if self._uniform_offsets is not None:
-#             self._add_offsets()
-
     set_verts = set_segments # for compatibility with PolyCollection
 
-    # MGDTODO: Support offsets (maybe)
-#     def _add_offsets(self):
-#         segs = self._segments
-#         offsets = self._uniform_offsets
-#         Nsegs = len(segs)
-#         Noffs = offsets.shape[0]
-#         if Noffs == 1:
-#             for i in range(Nsegs):
-#                 segs[i] = segs[i] + i * offsets
-#         else:
-#             for i in range(Nsegs):
-#                 io = i%Noffs
-#                 segs[i] = segs[i] + offsets[io:io+1]
+    def _add_offsets(self, segs):
+        offsets = self._uniform_offsets
+        Nsegs = len(segs)
+        Noffs = offsets.shape[0]
+        if Noffs == 1:
+            for i in range(Nsegs):
+                segs[i] = segs[i] + i * offsets
+        else:
+            for i in range(Nsegs):
+                io = i%Noffs
+                segs[i] = segs[i] + offsets[io:io+1]
+        return segs
 
-    def draw(self, renderer):
-        if not self.get_visible(): return
-        renderer.open_group('linecollection')
-        transform = self.get_transform()
-        # MGDTODO: Deal with offsets (maybe)
-#         transoffset = self.get_transoffset()
-
-        # segments = self._segments
-        # MGDTODO: Deal with offsets (maybe)
-        # offsets = self._offsets
-
-        # MGDTODO: Transform the paths (since we don't keep track of segments 
anymore
-        if self.have_units():
-            segments = []
-            for segment in self._segments:
-                xs, ys = zip(*segment)
-                xs = self.convert_xunits(xs)
-                ys = self.convert_yunits(ys)
-                segments.append(zip(xs, ys))
-#             if self._offsets is not None:
-#                 xs = self.convert_xunits(self._offsets[:0])
-#                 ys = self.convert_yunits(self._offsets[:1])
-#                 offsets = zip(xs, ys)
-                
-        self.update_scalarmappable()
-        #print 'calling renderer draw line collection'
-        clippath, clippath_trans = self.get_transformed_clip_path_and_affine()
-        renderer.draw_path_collection(
-            transform, self.clipbox, clippath, clippath_trans,
-            self._paths, [None], [None], 
-            self._colors, self._lw, self._ls, self._aa)
-        renderer.close_group('linecollection')
-
-    def set_linewidth(self, lw):
-        """
-        Set the linewidth(s) for the collection.  lw can be a scalar or a
-        sequence; if it is a sequence the patches will cycle through the
-        sequence
-
-        ACCEPTS: float or sequence of floats
-        """
-
-        self._lw = self._get_value(lw)
-
-    def set_linestyles(self, ls):
-        """
-        Set the linestyles(s) for the collection.
-        ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' |  (offset, 
on-off-dash-seq) ]
-        """
-        try:
-            if cbook.is_string_like(ls):
-                dashes = [backend_bases.GraphicsContextBase.dashd[ls]]
-            elif cbook.iterable(ls):
-                try:
-                    dashes = []
-                    for x in ls:
-                        if cbook.is_string_like(x):
-                            
dashes.append(backend_bases.GraphicsContextBase.dashd[ls])
-                        elif cbook.iterator(x) and len(x) == 2:
-                            dashes.append(x)
-                        else:
-                            raise ValueError()
-                except ValueError:
-                    if len(ls)==2:
-                        dashes = ls
-                    else:
-                        raise ValueError()
-            else:
-                raise ValueError()
-        except ValueError:
-            raise ValueError('Do not know how to convert %s to dashes'%ls)
-
-        self._ls = dashes
-
     def set_color(self, c):
         """
         Set the color(s) of the line collection.  c can be a
@@ -839,7 +716,7 @@
 
         ACCEPTS: matplotlib color arg or sequence of rgba tuples
         """
-        self._colors = _colors.colorConverter.to_rgba_list(c)
+        self._edgecolors = _colors.colorConverter.to_rgba_list(c)
 
     def color(self, c):
         """
@@ -853,48 +730,12 @@
         warnings.warn('LineCollection.color deprecated; use set_color instead')
         return self.set_color(c)
 
-    def set_alpha(self, alpha):
-        """
-        Set the alpha tranpancies of the collection.  Alpha can be a
-        float, in which case it is applied to the entire collection,
-        or a sequence of floats
-
-        ACCEPTS: float or sequence of floats
-        """
-
-        try: float(alpha)
-        except TypeError: raise TypeError('alpha must be a float')
-        else:
-            artist.Artist.set_alpha(self, alpha)
-            self._colors = [(r,g,b,alpha) for r,g,b,a in self._colors]
-
-    def get_linewidth(self):
-        return self._lw
-
-    def get_linestyle(self):
-        return self._ls
-
-    def get_dashes(self):
-        return self._ls
-
     def get_color(self):
-        return self._colors
+        return self._edgecolors
     get_colors = get_color  # for compatibility with old versions
 
-    def update_scalarmappable(self):
-        """
-        If the scalar mappable array is not none, update colors
-        from scalar data
-        """
-        if self._A is None: return
-        if len(self._A.shape)>1:
-            raise ValueError('LineCollections can only map rank 1 arrays')
-        self._colors = self.to_rgba(self._A, self._alpha)
 
-
-
-artist.kwdocd['Collection'] = artist.kwdoc(Collection)
-artist.kwdocd['PatchCollection'] = patchstr = artist.kwdoc(PatchCollection)
+artist.kwdocd['Collection'] = patchstr = artist.kwdoc(Collection)
 for k in ('QuadMesh', 'PolyCollection', 'BrokenBarHCollection', 
'RegularPolyCollection',
           'StarPolygonCollection'):
     artist.kwdocd[k] = patchstr

Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py        2007-10-08 12:45:23 UTC 
(rev 3927)
+++ branches/transforms/lib/matplotlib/legend.py        2007-10-08 18:10:11 UTC 
(rev 3928)
@@ -32,7 +32,7 @@
 from lines import Line2D
 from mlab import segments_intersect
 from patches import Patch, Rectangle, RegularPolygon, Shadow, bbox_artist, 
draw_bbox
-from collections import LineCollection, RegularPolyCollection, PatchCollection
+from collections import LineCollection, RegularPolyCollection
 from text import Text
 from transforms import Affine2D, Bbox, BboxTransform
 

Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-10-08 12:45:23 UTC (rev 
3927)
+++ branches/transforms/lib/matplotlib/lines.py 2007-10-08 18:10:11 UTC (rev 
3928)
@@ -349,24 +349,14 @@
         self._picker = p
 
     def get_window_extent(self, renderer):
-        # MGDTODO: Numpify
-        xy = self.get_transform().transform(self._xy)
+        bbox = Bbox()
+        bbox.update_from_data(self.get_transform().transform(self._xy))
 
-       x = xy[:, 0]
-       y = xy[:, 1]
-        left = x.min()
-        bottom = y.min()
-        width = x.max() - left
-        height = y.max() - bottom
-
         # correct for marker size, if any
         if self._marker is not None:
             ms = self._markersize / 72.0 * self.figure.dpi
-            left -= ms/2
-            bottom -= ms/2
-            width += ms
-            height += ms
-        return Bbox.from_lbwh(left, bottom, width, height)
+            bbox = Bbox(bbox.get_points() + [[-ms/2.0, ms/2.0]])
+        return bbox
 
 
     def set_axes(self, ax):

Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py    2007-10-08 12:45:23 UTC 
(rev 3927)
+++ branches/transforms/lib/matplotlib/transforms.py    2007-10-08 18:10:11 UTC 
(rev 3928)
@@ -395,6 +395,8 @@
            when False, include the existing bounds of the Bbox.
            when None, use the last value passed to Bbox.ignore().
         """
+        # MGDTODO: It may be more efficient for some callers to use 
update_from_data_xy instead
+
         if ignore is None:
             ignore = self._ignore
 
@@ -430,6 +432,18 @@
                 npy.float_)
             self._minpos = npy.minimum(minpos, self._minpos)
         self.invalidate()
+
+    def update_from_data_xy(self, xy, ignore=None):
+        """
+        Update the bounds of the Bbox based on the passed in data.
+
+        xy: a numpy array of 2D points
+        ignore:
+           when True, ignore the existing bounds of the Bbox.
+           when False, include the existing bounds of the Bbox.
+           when None, use the last value passed to Bbox.ignore().
+        """
+        return self.update_from_data(xy[:, 0], xy[:, 1], ignore)
         
     def _set_xmin(self, val):
         self._points[0, 0] = val

Modified: branches/transforms/setupext.py
===================================================================
--- branches/transforms/setupext.py     2007-10-08 12:45:23 UTC (rev 3927)
+++ branches/transforms/setupext.py     2007-10-08 18:10:11 UTC (rev 3928)
@@ -352,6 +352,7 @@
     try_pkgconfig(module, 'libpng', 'png')
     module.libraries.append('z')
     add_base_flags(module)
+    add_numpy_flags(module)
     module.include_dirs.extend(['src','swig', '%s/include'%AGG_VERSION, '.'])
 
     # put these later for correct link order
@@ -823,7 +824,7 @@
 
     # add agg flags before pygtk because agg only supports freetype1
     # and pygtk includes freetype2.  This is a bit fragile.
-
+    
     add_tk_flags(module) # do this first
     add_agg_flags(module)
     add_ft2font_flags(module)

Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp    2007-10-08 12:45:23 UTC (rev 
3927)
+++ branches/transforms/src/_backend_agg.cpp    2007-10-08 18:10:11 UTC (rev 
3928)
@@ -336,9 +336,11 @@
   if (bbox_obj.ptr() != Py_None) {
     PyArrayObject* bbox = (PyArrayObject*) PyArray_FromObject(bbox_obj.ptr(), 
PyArray_DOUBLE, 2, 2);   
 
-    if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || 
bbox->dimensions[1] != 2)
+    if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || 
bbox->dimensions[1] != 2) {
+      Py_XDECREF(bbox);
       throw Py::TypeError
        ("Expected a Bbox object.");
+    }
     
     *l       = *(double*)PyArray_GETPTR2(bbox, 0, 0);
     double _b = *(double*)PyArray_GETPTR2(bbox, 0, 1);
@@ -346,8 +348,11 @@
     double _t = *(double*)PyArray_GETPTR2(bbox, 1, 1);
     *b       = height - _t;
     *t       = height - _b;
+
+    Py_XDECREF(bbox);
     return true;
   }
+
   return false;
 }
 
@@ -920,7 +925,7 @@
 Py::Object
 RendererAgg::draw_path_collection(const Py::Tuple& args) {
   _VERBOSE("RendererAgg::draw_path_collection");
-  args.verify_length(11);
+  args.verify_length(13);
   
   //segments, trans, clipbox, colors, linewidths, antialiaseds
   agg::trans_affine      master_transform = 
py_to_agg_transformation_matrix(args[0]);
@@ -929,23 +934,30 @@
   agg::trans_affine       clippath_trans   = 
py_to_agg_transformation_matrix(args[3], false);
   Py::SeqBase<Py::Object> paths                   = args[4];
   Py::SeqBase<Py::Object> transforms_obj   = args[5];
-  Py::SeqBase<Py::Object> facecolors_obj   = args[6];
-  Py::SeqBase<Py::Object> edgecolors_obj   = args[7];
-  Py::SeqBase<Py::Float>  linewidths      = args[8];
-  Py::SeqBase<Py::Object> linestyles_obj   = args[9];
-  Py::SeqBase<Py::Int>    antialiaseds    = args[10];
+  Py::Object              offsets_obj      = args[6];
+  agg::trans_affine       offset_trans     = 
py_to_agg_transformation_matrix(args[7], false);
+  Py::SeqBase<Py::Object> facecolors_obj   = args[8];
+  Py::SeqBase<Py::Object> edgecolors_obj   = args[9];
+  Py::SeqBase<Py::Float>  linewidths      = args[10];
+  Py::SeqBase<Py::Object> linestyles_obj   = args[11];
+  Py::SeqBase<Py::Int>    antialiaseds    = args[12];
   
   GCAgg gc(dpi, false);
 
+  PyArrayObject* offsets = 
(PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2);
+  if (!offsets || offsets->dimensions[1] != 2)
+    throw Py::ValueError("Offsets array must be Nx2");
+
   size_t Npaths             = paths.length();
-  size_t Ntransforms = std::min(transforms_obj.length(), Npaths);
-  size_t Nfacecolors = std::min(facecolors_obj.length(), Npaths);
-  size_t Nedgecolors = std::min(edgecolors_obj.length(), Npaths);
+  size_t Noffsets    = offsets->dimensions[0];
+  size_t N          = std::max(Npaths, Noffsets);
+  size_t Ntransforms = std::min(transforms_obj.length(), N);
+  size_t Nfacecolors = std::min(facecolors_obj.length(), N);
+  size_t Nedgecolors = std::min(edgecolors_obj.length(), N);
   size_t Nlinewidths = linewidths.length();
-  size_t Nlinestyles = std::min(linestyles_obj.length(), Npaths);
+  size_t Nlinestyles = std::min(linestyles_obj.length(), N);
   size_t Naa        = antialiaseds.length();
 
-  size_t N       = Npaths;
   size_t i        = 0;
   
   // Convert all of the transforms up front
@@ -983,7 +995,7 @@
     }
   }
 
-  // Convert all of the edges up front
+  // Convert all of the edgecolors up front
   typedef std::vector<agg::rgba> edgecolors_t;
   edgecolors_t edgecolors;
   edgecolors.reserve(Nedgecolors);
@@ -1016,18 +1028,23 @@
   bool has_clippath = render_clippath(clippath, clippath_trans);
 
   for (i = 0; i < N; ++i) {
-    facepair_t& face = facecolors[i % Nfacecolors];
-    gc.color = edgecolors[i % Nedgecolors];
-    gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0;
-    gc.dashes = dashes[i % Nlinestyles].second;
-    gc.dashOffset = dashes[i % Nlinestyles].first;
-    gc.isaa = bool(Py::Int(antialiaseds[i % Naa]));
+    double xo                = *(double*)PyArray_GETPTR2(offsets, i % 
Noffsets, 0);
+    double yo                = *(double*)PyArray_GETPTR2(offsets, i % 
Noffsets, 1);
+    offset_trans.transform(&xo, &yo);
+    agg::trans_affine_translation transOffset(xo, yo);
     agg::trans_affine& trans = transforms[i % Ntransforms];
-    PathIterator path(paths[i]);
+    facepair_t& face         = facecolors[i % Nfacecolors];
+    gc.color                = edgecolors[i % Nedgecolors];
+    gc.linewidth            = double(Py::Float(linewidths[i % Nlinewidths])) * 
dpi/72.0;
+    gc.dashes               = dashes[i % Nlinestyles].second;
+    gc.dashOffset           = dashes[i % Nlinestyles].first;
+    gc.isaa                 = bool(Py::Int(antialiaseds[i % Naa]));
+    PathIterator path(paths[i % Npaths]);
     bool snap = (path.total_vertices() == 2);
-    _draw_path(path, trans, snap, has_clippath, face, gc);
+    _draw_path(path, trans * transOffset, snap, has_clippath, face, gc);
   }
 
+  Py_XDECREF(offsets);
   return Py::Object();
 }
 
@@ -1482,11 +1499,9 @@
 
 void get_path_extents(PathIterator& path, agg::trans_affine& trans, 
                      double* x0, double* y0, double* x1, double* y1) {
-  typedef agg::conv_transform<PathIterator> transformed_path_t;
-  typedef agg::conv_curve<transformed_path_t> curve_t;
+  typedef agg::conv_curve<PathIterator> curve_t;
   
-  transformed_path_t trans_path(path, trans);
-  curve_t curved_path(trans_path);
+  curve_t curved_path(path);
   double x, y;
   curved_path.rewind(0);
 
@@ -1505,6 +1520,9 @@
     if (x > *x1) *x1 = x;
     if (y > *y1) *y1 = y;
   }
+
+  trans.transform(x0, y0);
+  trans.transform(x1, y1);
 }
 
 Py::Object _backend_agg_module::get_path_extents(const Py::Tuple& args) {
@@ -1524,6 +1542,133 @@
   return result;
 }
 
+struct PathCollectionExtents {
+  double x0, y0, x1, y1;
+};
+
+Py::Object _backend_agg_module::get_path_collection_extents(const Py::Tuple& 
args) {
+  args.verify_length(5);
+
+  //segments, trans, clipbox, colors, linewidths, antialiaseds
+  agg::trans_affine      master_transform = 
py_to_agg_transformation_matrix(args[0]);
+  Py::SeqBase<Py::Object> paths                   = args[1];
+  Py::SeqBase<Py::Object> transforms_obj   = args[2];
+  Py::SeqBase<Py::Object> offsets          = args[3];
+  agg::trans_affine       offset_trans     = 
py_to_agg_transformation_matrix(args[4], false);
+
+  size_t Npaths             = paths.length();
+  size_t Noffsets    = offsets.length();
+  size_t N          = std::max(Npaths, Noffsets);
+  size_t Ntransforms = std::min(transforms_obj.length(), N);
+  size_t i;
+
+  // Convert all of the transforms up front
+  typedef std::vector<agg::trans_affine> transforms_t;
+  transforms_t transforms;
+  transforms.reserve(Ntransforms);
+  for (i = 0; i < Ntransforms; ++i) {
+    agg::trans_affine trans = py_to_agg_transformation_matrix
+      (transforms_obj[i], false);
+    trans *= master_transform;
+    transforms.push_back(trans);
+  }
+  
+  typedef std::vector<PathCollectionExtents> path_extents_t;
+  path_extents_t path_extents;
+  path_extents.resize(Npaths);
+
+  // Get each of the path extents first
+  i = 0;
+  for (path_extents_t::iterator p = path_extents.begin();
+       p != path_extents.end(); ++p, ++i) {
+    PathIterator path(paths[i]);
+    agg::trans_affine& trans = transforms[i % Ntransforms];
+    ::get_path_extents(path, trans, &p->x0, &p->y0, &p->x1, &p->y1);
+  }
+
+  // The offset each of those and collect the mins/maxs
+  double x0 = std::numeric_limits<double>::infinity();
+  double y0 = std::numeric_limits<double>::infinity();
+  double x1 = -std::numeric_limits<double>::infinity();
+  double y1 = -std::numeric_limits<double>::infinity();
+  for (i = 0; i < N; ++i) {
+    Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % 
Noffsets]);
+    double xo                = Py::Float(offset[0]);
+    double yo                = Py::Float(offset[1]);
+    offset_trans.transform(&xo, &yo);
+    PathCollectionExtents& ext = path_extents[i % Npaths];
+
+    x0 = std::min(x0, ext.x0 + xo);
+    y0 = std::min(y0, ext.y0 + yo);
+    x1 = std::max(x1, ext.x1 + xo);
+    y1 = std::max(y1, ext.y1 + yo);
+  }
+
+  Py::Tuple result(4);
+  result[0] = Py::Float(x0);
+  result[1] = Py::Float(y0);
+  result[2] = Py::Float(x1);
+  result[3] = Py::Float(y1);
+  return result;
+}
+
+Py::Object _backend_agg_module::point_in_path_collection(const Py::Tuple& 
args) {
+  args.verify_length(9);
+
+  //segments, trans, clipbox, colors, linewidths, antialiaseds
+  double                 x                = Py::Float(args[0]);
+  double                 y                = Py::Float(args[1]);
+  double                  radius           = Py::Float(args[2]);
+  agg::trans_affine      master_transform = 
py_to_agg_transformation_matrix(args[3]);
+  Py::SeqBase<Py::Object> paths                   = args[4];
+  Py::SeqBase<Py::Object> transforms_obj   = args[5];
+  Py::SeqBase<Py::Object> offsets          = args[6];
+  agg::trans_affine       offset_trans     = 
py_to_agg_transformation_matrix(args[7], false);
+  Py::SeqBase<Py::Object> facecolors       = args[8];
+  
+  size_t Npaths             = paths.length();
+  size_t Noffsets    = offsets.length();
+  size_t N          = std::max(Npaths, Noffsets);
+  size_t Ntransforms = std::min(transforms_obj.length(), N);
+  size_t Ncolors     = facecolors.length();
+  size_t i;
+
+  // Convert all of the transforms up front
+  typedef std::vector<agg::trans_affine> transforms_t;
+  transforms_t transforms;
+  transforms.reserve(Ntransforms);
+  for (i = 0; i < Ntransforms; ++i) {
+    agg::trans_affine trans = py_to_agg_transformation_matrix
+      (transforms_obj[i], false);
+    trans *= master_transform;
+    transforms.push_back(trans);
+  }
+
+  Py::List result;
+
+  for (i = 0; i < N; ++i) {
+    PathIterator path(paths[i % Npaths]);
+    
+    Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % 
Noffsets]);
+    double xo                = Py::Float(offset[0]);
+    double yo                = Py::Float(offset[1]);
+    offset_trans.transform(&xo, &yo);
+    agg::trans_affine_translation transOffset(xo, yo);
+    agg::trans_affine trans = transforms[i % Ntransforms] * transOffset;
+
+    const Py::Object& facecolor_obj = facecolors[i & Ncolors];
+    if (facecolor_obj.ptr() == Py_None) {
+      if (::point_on_path(x, y, radius, path, trans))
+       result.append(Py::Int((int)i));
+    } else {
+      if (::point_in_path(x, y, path, trans))
+       result.append(Py::Int((int)i));
+    }
+  }
+
+  return result;
+}
+
 /* ------------ module methods ------------- */
 Py::Object _backend_agg_module::new_renderer (const Py::Tuple &args,
                                              const Py::Dict &kws)
@@ -1563,7 +1708,7 @@
   add_varargs_method("draw_path", &RendererAgg::draw_path,
                     "draw_path(gc, path, transform, rgbFace)\n");
   add_varargs_method("draw_path_collection", 
&RendererAgg::draw_path_collection,
-                    "draw_path_collection(master_transform, cliprect, 
clippath, clippath_trans, paths, transforms, facecolors, edgecolors, 
linewidths, linestyles, antialiaseds)\n");
+                    "draw_path_collection(master_transform, cliprect, 
clippath, clippath_trans, paths, transforms, offsets, offsetTrans, facecolors, 
edgecolors, linewidths, linestyles, antialiaseds)\n");
   add_varargs_method("draw_markers", &RendererAgg::draw_markers,
                     "draw_markers(gc, marker_path, marker_trans, path, 
rgbFace)\n");
   add_varargs_method("draw_text_image", &RendererAgg::draw_text_image,

Modified: branches/transforms/src/_backend_agg.h
===================================================================
--- branches/transforms/src/_backend_agg.h      2007-10-08 12:45:23 UTC (rev 
3927)
+++ branches/transforms/src/_backend_agg.h      2007-10-08 18:10:11 UTC (rev 
3928)
@@ -244,6 +244,11 @@
                       "point_on_path(x, y, r, path, trans)");
     add_varargs_method("get_path_extents", 
&_backend_agg_module::get_path_extents,
                       "get_path_extents(path, trans)");
+    add_varargs_method("get_path_collection_extents", 
&_backend_agg_module::get_path_collection_extents,
+                      "get_path_collection_extents(trans, paths, transforms, 
offsets, offsetTrans)");
+    add_varargs_method("point_in_path_collection", 
&_backend_agg_module::point_in_path_collection,
+                      "point_in_path_collection(x, y, r, trans, paths, 
transforms, offsets, offsetTrans, colors)");
+
     initialize( "The agg rendering backend" );
   }
 
@@ -255,6 +260,8 @@
   Py::Object point_in_path(const Py::Tuple& args);
   Py::Object point_on_path(const Py::Tuple& args);
   Py::Object get_path_extents(const Py::Tuple& args);
+  Py::Object get_path_collection_extents(const Py::Tuple& args);
+  Py::Object point_in_path_collection(const Py::Tuple& args);
 };
 
 


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to