Revision: 6708
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6708&view=rev
Author:   mdboom
Date:     2008-12-29 14:08:13 +0000 (Mon, 29 Dec 2008)

Log Message:
-----------
Merge branch 'hatching'

Modified Paths:
--------------
    trunk/matplotlib/examples/pylab_examples/hatch_demo.py
    trunk/matplotlib/lib/matplotlib/backend_bases.py
    trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
    trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
    trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
    trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
    trunk/matplotlib/lib/matplotlib/path.py
    trunk/matplotlib/src/_backend_agg.cpp
    trunk/matplotlib/src/_backend_agg.h

Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/hatch_demo.py      2008-12-29 
13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py      2008-12-29 
14:08:13 UTC (rev 6708)
@@ -12,7 +12,7 @@
              xytext=(0, 5),
              xycoords="axes fraction", textcoords="offset points", ha="center"
              )
-ax1.bar(range(1,5), range(1,5), color='gray', ecolor='black', hatch="/")
+ax1.bar(range(1,5), range(1,5), color='gray', edgecolor='red', hatch="/")
 
 
 ax2 = fig.add_subplot(122)
@@ -23,3 +23,5 @@
      bar.set_hatch(pattern)
 
 plt.show()
+plt.savefig("test.pdf")
+plt.savefig("test.ps")

Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backend_bases.py    2008-12-29 13:58:18 UTC 
(rev 6707)
+++ trunk/matplotlib/lib/matplotlib/backend_bases.py    2008-12-29 14:08:13 UTC 
(rev 6708)
@@ -30,6 +30,7 @@
 import matplotlib.colors as colors
 import matplotlib.transforms as transforms
 import matplotlib.widgets as widgets
+import matplotlib.path as path
 from matplotlib import rcParams
 
 class RendererBase:
@@ -679,6 +680,14 @@
         """
         return self._hatch
 
+    def get_hatch_path(self, density=6.0):
+        """
+        Returns a Path for the current hatch.
+        """
+        if self._hatch is None:
+            return None
+        return path.Path.hatch(self._hatch, density)
+
 class Event:
     """
     A matplotlib event.  Attach additional attributes as defined in

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py     2008-12-29 
13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py     2008-12-29 
14:08:13 UTC (rev 6708)
@@ -73,9 +73,13 @@
                                      'debug-annoying')
 
     def draw_path(self, gc, path, transform, rgbFace=None):
+        """
+        Draw the path
+        """
         nmax = rcParams['agg.path.chunksize'] # here at least for testing
         npts = path.vertices.shape[0]
-        if nmax > 100 and npts > nmax and path.should_simplify and rgbFace is 
None:
+        if (nmax > 100 and npts > nmax and path.should_simplify and
+            rgbFace is None and gc.get_hatch() is None):
             nch = npy.ceil(npts/float(nmax))
             chsize = int(npy.ceil(npts/nch))
             i0 = npy.arange(0, npts, chsize)
@@ -93,7 +97,6 @@
         else:
             self._renderer.draw_path(gc, path, transform, rgbFace)
 
-
     def draw_mathtext(self, gc, x, y, s, prop, angle):
         """
         Draw the math text using matplotlib.mathtext

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2008-12-29 
13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2008-12-29 
14:08:13 UTC (rev 6708)
@@ -953,21 +953,20 @@
                      'CA': alpha, 'ca': alpha })
         return name
 
-    def hatchPattern(self, lst):
-        pattern = self.hatchPatterns.get(lst, None)
+    def hatchPattern(self, hatch_style):
+        pattern = self.hatchPatterns.get(hatch_style, None)
         if pattern is not None:
             return pattern
 
         name = Name('H%d' % self.nextHatch)
         self.nextHatch += 1
-        self.hatchPatterns[lst] = name
+        self.hatchPatterns[hatch_style] = name
         return name
 
     def writeHatches(self):
         hatchDict = dict()
-        sidelen = 144.0
-        density = 24.0
-        for lst, name in self.hatchPatterns.items():
+        sidelen = 72.0
+        for hatch_style, name in self.hatchPatterns.items():
             ob = self.reserveObject('hatch pattern')
             hatchDict[name] = ob
             res = { 'Procsets':
@@ -983,33 +982,21 @@
             # lst is a tuple of stroke color, fill color,
             # number of - lines, number of / lines,
             # number of | lines, number of \ lines
-            rgb = lst[0]
+            rgb = hatch_style[0]
             self.output(rgb[0], rgb[1], rgb[2], Op.setrgb_stroke)
-            if lst[1] is not None:
-                rgb = lst[1]
+            if hatch_style[1] is not None:
+                rgb = hatch_style[1]
                 self.output(rgb[0], rgb[1], rgb[2], Op.setrgb_nonstroke,
                             0, 0, sidelen, sidelen, Op.rectangle,
                             Op.fill)
-            if lst[2]:                # -
-                for j in npy.arange(0.0, sidelen, density/lst[2]):
-                    self.output(0, j, Op.moveto,
-                                sidelen, j, Op.lineto)
-            if lst[3]:                # /
-                for j in npy.arange(0.0, sidelen, density/lst[3]):
-                    self.output(0, j, Op.moveto,
-                                sidelen-j, sidelen, Op.lineto,
-                                sidelen-j, 0, Op.moveto,
-                                sidelen, j, Op.lineto)
-            if lst[4]:                # |
-                for j in npy.arange(0.0, sidelen, density/lst[4]):
-                    self.output(j, 0, Op.moveto,
-                                j, sidelen, Op.lineto)
-            if lst[5]:                # \
-                for j in npy.arange(sidelen, 0.0, -density/lst[5]):
-                    self.output(sidelen, j, Op.moveto,
-                                j, sidelen, Op.lineto,
-                                j, 0, Op.moveto,
-                                0, j, Op.lineto)
+
+            self.output(0.1, Op.setlinewidth)
+
+            # TODO: We could make this dpi-dependent, but that would be
+            # an API change
+            self.output(*self.pathOperations(
+                    Path.hatch(hatch_style[2]),
+                    Affine2D().scale(sidelen)))
             self.output(Op.stroke)
 
             self.endStream()
@@ -1735,13 +1722,8 @@
                 return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
         else:
             hatch = hatch.lower()
-            lst = ( self._rgb,
-                    self._fillcolor,
-                    hatch.count('-') + hatch.count('+'),
-                    hatch.count('/') + hatch.count('x'),
-                    hatch.count('|') + hatch.count('+'),
-                    hatch.count('\\') + hatch.count('x') )
-            name = self.file.hatchPattern(lst)
+            hatch_style = (self._rgb, self._fillcolor, hatch)
+            name = self.file.hatchPattern(hatch_style)
             return [Name('Pattern'), Op.setcolorspace_nonstroke,
                     name, Op.setcolor_nonstroke]
 

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py      2008-12-29 
13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py      2008-12-29 
14:08:13 UTC (rev 6708)
@@ -31,7 +31,7 @@
 from matplotlib._mathtext_data import uni2type1
 from matplotlib.text import Text
 from matplotlib.path import Path
-from matplotlib.transforms import IdentityTransform
+from matplotlib.transforms import Affine2D
 
 import numpy as npy
 import binascii
@@ -163,7 +163,7 @@
         self.linedash = None
         self.fontname = None
         self.fontsize = None
-        self.hatch = None
+        self._hatches = {}
         self.image_magnification = imagedpi/72.0
         self._clip_paths = {}
         self._path_collection_id = 0
@@ -231,58 +231,36 @@
             if store: self.fontname = fontname
             if store: self.fontsize = fontsize
 
-    def set_hatch(self, hatch):
-        """
-        hatch can be one of:
-            /   - diagonal hatching
-            \   - back diagonal
-            |   - vertical
-            -   - horizontal
-            +   - crossed
-            X   - crossed diagonal
+    def create_hatch(self, hatch):
+        sidelen = 72
+        if self._hatches.has_key(hatch):
+            return self._hatches[hatch]
+        name = 'H%d' % len(self._hatches)
+        self._pswriter.write("""\
+  << /PatternType 1
+     /PaintType 2
+     /TilingType 2
+     /BBox[0 0 %(sidelen)d %(sidelen)d]
+     /XStep %(sidelen)d
+     /YStep %(sidelen)d
 
-        letters can be combined, in which case all the specified
-        hatchings are done
+     /PaintProc {
+        pop
+        0 setlinewidth
+""" % locals())
+        self._pswriter.write(
+            self._convert_path(Path.hatch(hatch), Affine2D().scale(72.0)))
+        self._pswriter.write("""\
+          stroke
+     } bind
+   >>
+   matrix
+   makepattern
+   /%(name)s exch def
+""" % locals())
+        self._hatches[hatch] = name
+        return name
 
-        if same letter repeats, it increases the density of hatching
-        in that direction
-        """
-        hatches = {'horiz':0, 'vert':0, 'diag1':0, 'diag2':0}
-
-        for letter in hatch:
-            if   (letter == '/'):    hatches['diag2'] += 1
-            elif (letter == '\\'):   hatches['diag1'] += 1
-            elif (letter == '|'):    hatches['vert']  += 1
-            elif (letter == '-'):    hatches['horiz'] += 1
-            elif (letter == '+'):
-                hatches['horiz'] += 1
-                hatches['vert'] += 1
-            elif (letter.lower() == 'x'):
-                hatches['diag1'] += 1
-                hatches['diag2'] += 1
-
-        def do_hatch(angle, density):
-            if (density == 0): return ""
-            return """\
-  gsave
-   eoclip %s rotate 0.0 0.0 0.0 0.0 setrgbcolor 0 setlinewidth
-   /hatchgap %d def
-   pathbbox /hatchb exch def /hatchr exch def /hatcht exch def /hatchl exch def
-   hatchl cvi hatchgap idiv hatchgap mul
-   hatchgap
-   hatchr cvi hatchgap idiv hatchgap mul
-   {hatcht m 0 hatchb hatcht sub r }
-   for
-   stroke
-  grestore
- """ % (angle, 12/density)
-        self._pswriter.write("gsave\n")
-        self._pswriter.write(do_hatch(90, hatches['horiz']))
-        self._pswriter.write(do_hatch(0, hatches['vert']))
-        self._pswriter.write(do_hatch(45, hatches['diag1']))
-        self._pswriter.write(do_hatch(-45, hatches['diag2']))
-        self._pswriter.write("grestore\n")
-
     def get_canvas_width_height(self):
         'return the canvas width and height in display coords'
         return self.width, self.height
@@ -816,15 +794,17 @@
         if fill:
             if stroke:
                 write("gsave\n")
-                self.set_color(store=0, *rgbFace[:3])
-                write("fill\ngrestore\n")
-            else:
-                self.set_color(store=0, *rgbFace[:3])
-                write("fill\n")
+            self.set_color(store=0, *rgbFace[:3])
+            write("fill\n")
+            if stroke:
+                write("grestore\n")
 
         hatch = gc.get_hatch()
         if hatch:
-            self.set_hatch(hatch)
+            hatch_name = self.create_hatch(hatch)
+            write("gsave\n")
+            write("[/Pattern [/DeviceRGB]] setcolorspace %f %f %f " % 
gc.get_rgb()[:3])
+            write("%s setcolor fill grestore\n" % hatch_name)
 
         if stroke:
             write("stroke\n")

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py     2008-12-29 
13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py     2008-12-29 
14:08:13 UTC (rev 6708)
@@ -57,6 +57,7 @@
         self._markers = {}
         self._path_collection_id = 0
         self._imaged = {}
+        self._hatchd = {}
         self.mathtext_parser = MathTextParser('SVG')
         svgwriter.write(svgProlog%(width,height,width,height))
 
@@ -90,15 +91,38 @@
         font.set_size(size, 72.0)
         return font
 
+    def _get_hatch(self, gc, rgbFace):
+        """
+        Create a new hatch pattern
+        """
+        HATCH_SIZE = 144
+        dictkey = (gc.get_hatch().lower(), rgbFace, gc.get_rgb())
+        id = self._hatchd.get(dictkey)
+        if id is None:
+            id = 'h%s' % md5(str(dictkey)).hexdigest()
+            self._svgwriter.write('<defs>\n  <pattern id="%s" ' % id)
+            self._svgwriter.write('patternUnits="userSpaceOnUse" x="0" y="0" ')
+            self._svgwriter.write(' width="%d" height="%d" >\n' % (HATCH_SIZE, 
HATCH_SIZE))
+            path_data = self._convert_path(gc.get_hatch_path(), 
Affine2D().scale(144))
+            path = '<path d="%s" fill="%s" stroke="%s" stroke-width="1.0"/>' % 
(
+                path_data, rgb2hex(rgbFace[:3]), rgb2hex(gc.get_rgb()[:3]))
+            self._svgwriter.write(path)
+            self._svgwriter.write('\n  </pattern>\n</defs>')
+            self._hatchd[dictkey] = id
+        return id
+
     def _get_style(self, gc, rgbFace):
         """
         return the style string.
         style is generated from the GraphicsContext, rgbFace and clippath
         """
-        if rgbFace is None:
-            fill = 'none'
+        if gc.get_hatch() is not None:
+            fill = "url(#%s)" % self._get_hatch(gc, rgbFace)
         else:
-            fill = rgb2hex(rgbFace[:3])
+            if rgbFace is None:
+                fill = 'none'
+            else:
+                fill = rgb2hex(rgbFace[:3])
 
         offset, seq = gc.get_dashes()
         if seq is None:
@@ -150,7 +174,7 @@
     def open_group(self, s, gid=None):
         """
         Open a grouping element with label *s*. If *gid* is given, use
-        *gid* as the id of the group. 
+        *gid* as the id of the group.
         """
         if gid:
             self._svgwriter.write('<g id="%s">\n' % (gid))

Modified: trunk/matplotlib/lib/matplotlib/path.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/path.py     2008-12-29 13:58:18 UTC (rev 
6707)
+++ trunk/matplotlib/lib/matplotlib/path.py     2008-12-29 14:08:13 UTC (rev 
6708)
@@ -11,7 +11,7 @@
 from matplotlib._path import point_in_path, get_path_extents, \
     point_in_path_collection, get_path_collection_extents, \
     path_in_path, path_intersects_path, convert_path_to_polygons
-from matplotlib.cbook import simple_linear_interpolation
+from matplotlib.cbook import simple_linear_interpolation, maxdict
 
 class Path(object):
     """
@@ -115,8 +115,8 @@
         self.codes = codes
         self.vertices = vertices
 
-    #...@staticmethod
-    def make_compound_path(*args):
+    #...@classmethod
+    def make_compound_path(cls, *args):
         """
         (staticmethod) Make a compound path from a list of Path
         objects.  Only polygons (not curves) are supported.
@@ -130,14 +130,14 @@
         vertices = np.vstack([x.vertices for x in args])
         vertices.reshape((total_length, 2))
 
-        codes = Path.LINETO * np.ones(total_length)
+        codes = cls.LINETO * np.ones(total_length)
         i = 0
         for length in lengths:
-            codes[i] = Path.MOVETO
+            codes[i] = cls.MOVETO
             i += length
 
-        return Path(vertices, codes)
-    make_compound_path = staticmethod(make_compound_path)
+        return cls(vertices, codes)
+    make_compound_path = classmethod(make_compound_path)
 
     def __repr__(self):
         return "Path(%s, %s)" % (self.vertices, self.codes)
@@ -343,7 +343,7 @@
         """
         if cls._unit_rectangle is None:
             cls._unit_rectangle = \
-                Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 
0.0]])
+                cls([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 
0.0]])
         return cls._unit_rectangle
     unit_rectangle = classmethod(unit_rectangle)
 
@@ -366,7 +366,7 @@
             # "points-up"
             theta += np.pi / 2.0
             verts = np.concatenate((np.cos(theta), np.sin(theta)), 1)
-            path = Path(verts)
+            path = cls(verts)
             cls._unit_regular_polygons[numVertices] = path
         return path
     unit_regular_polygon = classmethod(unit_regular_polygon)
@@ -392,7 +392,7 @@
             r = np.ones(ns2 + 1)
             r[1::2] = innerCircle
             verts = np.vstack((r*np.cos(theta), r*np.sin(theta))).transpose()
-            path = Path(verts)
+            path = cls(verts)
             cls._unit_regular_polygons[(numVertices, innerCircle)] = path
         return path
     unit_regular_star = classmethod(unit_regular_star)
@@ -466,7 +466,7 @@
             codes[0] = cls.MOVETO
             codes[-1] = cls.CLOSEPOLY
 
-            cls._unit_circle = Path(vertices, codes)
+            cls._unit_circle = cls(vertices, codes)
         return cls._unit_circle
     unit_circle = classmethod(unit_circle)
 
@@ -523,19 +523,19 @@
 
         if is_wedge:
             length = n * 3 + 4
-            vertices = np.zeros((length, 2), np.float_)
-            codes = Path.CURVE4 * np.ones((length, ), Path.code_type)
+            vertices = np.empty((length, 2), np.float_)
+            codes = cls.CURVE4 * np.ones((length, ), cls.code_type)
             vertices[1] = [xA[0], yA[0]]
-            codes[0:2] = [Path.MOVETO, Path.LINETO]
-            codes[-2:] = [Path.LINETO, Path.CLOSEPOLY]
+            codes[0:2] = [cls.MOVETO, cls.LINETO]
+            codes[-2:] = [cls.LINETO, cls.CLOSEPOLY]
             vertex_offset = 2
             end = length - 2
         else:
             length = n * 3 + 1
-            vertices = np.zeros((length, 2), np.float_)
-            codes = Path.CURVE4 * np.ones((length, ), Path.code_type)
+            vertices = np.empty((length, 2), np.float_)
+            codes = cls.CURVE4 * np.ones((length, ), cls.code_type)
             vertices[0] = [xA[0], yA[0]]
-            codes[0] = Path.MOVETO
+            codes[0] = cls.MOVETO
             vertex_offset = 1
             end = length
 
@@ -546,7 +546,7 @@
         vertices[vertex_offset+2:end:3, 0] = xB
         vertices[vertex_offset+2:end:3, 1] = yB
 
-        return Path(vertices, codes)
+        return cls(vertices, codes)
     arc = classmethod(arc)
 
     #...@classmethod
@@ -562,6 +562,94 @@
         return cls.arc(theta1, theta2, n, True)
     wedge = classmethod(wedge)
 
+    _hatch_dict = maxdict(8)
+    #...@classmethod
+    def hatch(cls, hatchpattern, density=6):
+        """
+        Given a hatch specifier, *hatchpattern*, generates a Path that
+        can be used in a repeated hatching pattern.  *density* is the
+        number of lines per unit square.
+        """
+        if hatchpattern is None:
+            return None
+
+        hatch = hatchpattern.lower()
+        hatch_path = cls._hatch_dict.get((hatch, density))
+        if hatch_path is not None:
+            return hatch_path
+
+        size = 1.0
+        density = int(density)
+        counts = [
+            hatch.count('-') + hatch.count('+'),
+            hatch.count('/') + hatch.count('x'),
+            hatch.count('|') + hatch.count('+'),
+            hatch.count('\\') + hatch.count('x')
+            ]
+
+        if sum(counts) == 0:
+            return cls([])
+
+        counts = [x * density for x in counts]
+
+        num_vertices = (counts[0] * 2 + counts[1] * 4 +
+                        counts[2] * 2 + counts[3] * 4)
+        vertices = np.empty((num_vertices, 2))
+        codes = np.empty((num_vertices,), cls.code_type)
+        codes[0::2] = cls.MOVETO
+        codes[1::2] = cls.LINETO
+
+        cursor = 0
+
+        if counts[0]:
+            vertices_chunk = vertices[cursor:cursor + counts[0] * 2]
+            cursor += counts[0] * 2
+            steps = np.linspace(0.0, 1.0, counts[0], False)
+            vertices_chunk[0::2, 0] = 0.0
+            vertices_chunk[0::2, 1] = steps
+            vertices_chunk[1::2, 0] = size
+            vertices_chunk[1::2, 1] = steps
+
+        if counts[1]:
+            vertices_chunk = vertices[cursor:cursor + counts[1] * 4]
+            cursor += counts[1] * 4
+            steps = np.linspace(0.0, 1.0, counts[1], False)
+            vertices_chunk[0::4, 0] = 0.0
+            vertices_chunk[0::4, 1] = steps
+            vertices_chunk[1::4, 0] = size - steps
+            vertices_chunk[1::4, 1] = size
+            vertices_chunk[2::4, 0] = size - steps
+            vertices_chunk[2::4, 1] = 0.0
+            vertices_chunk[3::4, 0] = size
+            vertices_chunk[3::4, 1] = steps
+
+        if counts[2]:
+            vertices_chunk = vertices[cursor:cursor + counts[2] * 2]
+            cursor += counts[2] * 2
+            steps = np.linspace(0.0, 1.0, counts[2], False)
+            vertices_chunk[0::2, 0] = steps
+            vertices_chunk[0::2, 1] = 0.0
+            vertices_chunk[1::2, 0] = steps
+            vertices_chunk[1::2, 1] = size
+
+        if counts[3]:
+            vertices_chunk = vertices[cursor:cursor + counts[3] * 4]
+            cursor += counts[3] * 4
+            steps = np.linspace(0.0, 1.0, counts[3], False)
+            vertices_chunk[0::4, 0] = size
+            vertices_chunk[0::4, 1] = steps
+            vertices_chunk[1::4, 0] = steps
+            vertices_chunk[1::4, 1] = size
+            vertices_chunk[2::4, 0] = steps
+            vertices_chunk[2::4, 1] = 0.0
+            vertices_chunk[3::4, 0] = 0.0
+            vertices_chunk[3::4, 1] = steps
+
+        hatch_path = cls(vertices, codes)
+        cls._hatch_dict[(hatch, density)] = hatch_path
+        return hatch_path
+    hatch = classmethod(hatch)
+
 _get_path_collection_extents = get_path_collection_extents
 def get_path_collection_extents(*args):
     """

Modified: trunk/matplotlib/src/_backend_agg.cpp
===================================================================
--- trunk/matplotlib/src/_backend_agg.cpp       2008-12-29 13:58:18 UTC (rev 
6707)
+++ trunk/matplotlib/src/_backend_agg.cpp       2008-12-29 14:08:13 UTC (rev 
6708)
@@ -30,6 +30,7 @@
 #include "agg_span_image_filter_gray.h"
 #include "agg_span_image_filter_rgba.h"
 #include "agg_span_interpolator_linear.h"
+#include "agg_span_pattern_rgba.h"
 #include "agg_conv_shorten_path.h"
 #include "util/agg_color_conv_rgb8.h"
 
@@ -149,6 +150,7 @@
   _set_clip_rectangle(gc);
   _set_clip_path(gc);
   _set_snap(gc);
+  _set_hatch_path(gc);
 }
 
 GCAgg::GCAgg(double dpi) :
@@ -273,6 +275,15 @@
   }
 }
 
+void
+GCAgg::_set_hatch_path( const Py::Object& gc) {
+  _VERBOSE("GCAgg::_set_hatch_path");
+
+  Py::Object method_obj = gc.getAttr("get_hatch_path");
+  Py::Callable method(method_obj);
+  hatchpath = method.apply(Py::Tuple());
+}
+
 const size_t
 RendererAgg::PIXELS_PER_INCH(96);
 
@@ -310,6 +321,7 @@
   rendererBase.clear(agg::rgba(1, 1, 1, 0));
   rendererAA.attach(rendererBase);
   rendererBin.attach(rendererBase);
+  hatchRenderingBuffer.attach(hatchBuffer, HATCH_SIZE, HATCH_SIZE, 
HATCH_SIZE*4);
 }
 
 void RendererAgg::create_alpha_buffers() {
@@ -879,6 +891,55 @@
     }
   }
 
+  // Render hatch
+  if (!gc.hatchpath.isNone()) {
+    // Reset any clipping that may be in effect, since we'll be
+    // drawing the hatch in a scratch buffer at origin (0, 0)
+    theRasterizer.reset_clipping();
+    rendererBase.reset_clipping(true);
+
+    // Create and transform the path
+    typedef agg::conv_transform<PathIterator> hatch_path_trans_t;
+    typedef SimplifyPath<hatch_path_trans_t> hatch_path_simplify_t;
+    typedef agg::conv_stroke<hatch_path_simplify_t> hatch_path_stroke_t;
+
+    PathIterator hatch_path(gc.hatchpath);
+    agg::trans_affine hatch_trans;
+    hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE);
+    hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans);
+    hatch_path_simplify_t hatch_path_simplify
+      (hatch_path_trans, true, false, HATCH_SIZE, HATCH_SIZE);
+    hatch_path_stroke_t hatch_path_stroke(hatch_path_simplify);
+    hatch_path_stroke.width(1.0);
+    hatch_path_stroke.line_cap(agg::square_cap);
+    theRasterizer.add_path(hatch_path_stroke);
+
+    // Render the path into the hatch buffer
+    pixfmt hatch_img_pixf(hatchRenderingBuffer);
+    renderer_base rb(hatch_img_pixf);
+    renderer_aa rs(rb);
+    rb.clear(agg::rgba(0.0, 0.0, 0.0, 0.0));
+    rs.color(gc.color);
+    agg::render_scanlines(theRasterizer, slineP8, rs);
+
+    // Put clipping back on, if originally set on entry to this
+    // function
+    set_clipbox(gc.cliprect, theRasterizer);
+    if (has_clippath)
+      render_clippath(gc.clippath, gc.clippath_trans);
+
+    // Transfer the hatch to the main image buffer
+    typedef agg::image_accessor_wrap<pixfmt,
+      agg::wrap_mode_repeat_auto_pow2,
+      agg::wrap_mode_repeat_auto_pow2> img_source_type;
+    typedef agg::span_pattern_rgba<img_source_type> span_gen_type;
+    agg::span_allocator<agg::rgba8> sa;
+    img_source_type img_src(hatch_img_pixf);
+    span_gen_type sg(img_src, 0, 0);
+    theRasterizer.add_path(path);
+    agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg);
+  }
+
   // Render stroke
   if (gc.linewidth != 0.0) {
     double linewidth = gc.linewidth;

Modified: trunk/matplotlib/src/_backend_agg.h
===================================================================
--- trunk/matplotlib/src/_backend_agg.h 2008-12-29 13:58:18 UTC (rev 6707)
+++ trunk/matplotlib/src/_backend_agg.h 2008-12-29 14:08:13 UTC (rev 6708)
@@ -60,7 +60,6 @@
 typedef agg::scanline_bin scanline_bin;
 typedef agg::amask_no_clip_gray8 alpha_mask_type;
 
-
 typedef agg::renderer_base<agg::pixfmt_gray8> renderer_base_alpha_mask_type;
 typedef agg::renderer_scanline_aa_solid<renderer_base_alpha_mask_type> 
renderer_alpha_mask_type;
 
@@ -129,6 +128,8 @@
     SNAP_TRUE
   } snap;
 
+  Py::Object hatchpath;
+
 protected:
   agg::rgba get_color(const Py::Object& gc);
   double points_to_pixels( const Py::Object& points);
@@ -139,6 +140,7 @@
   void _set_clip_path( const Py::Object& gc);
   void _set_antialiased( const Py::Object& gc);
   void _set_snap( const Py::Object& gc);
+  void _set_hatch_path( const Py::Object& gc);
 };
 
 
@@ -206,6 +208,12 @@
   Py::Object lastclippath;
   agg::trans_affine lastclippath_transform;
 
+  // HATCH_SIZE should be a power of 2, to take advantage of Agg's
+  // fast pattern rendering
+  static const size_t HATCH_SIZE = 128;
+  agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4];
+  agg::rendering_buffer hatchRenderingBuffer;
+
   const int debug;
 
 protected:


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

------------------------------------------------------------------------------
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to