Revision: 6744
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6744&view=rev
Author:   mdboom
Date:     2009-01-06 17:24:32 +0000 (Tue, 06 Jan 2009)

Log Message:
-----------
Add more hatch styles.  Improve consistency across backends.

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

Added Paths:
-----------
    trunk/matplotlib/lib/matplotlib/hatch.py

Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/hatch_demo.py      2009-01-06 
17:06:06 UTC (rev 6743)
+++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py      2009-01-06 
17:24:32 UTC (rev 6744)
@@ -6,17 +6,18 @@
 
 fig = plt.figure()
 ax1 = fig.add_subplot(121)
-ax1.annotate("Hatch is only supported in the PS and PDF backend", (1, 1),
+ax1.annotate("Hatch is only supported in the PS, PDF, SVG and Agg backends", 
(1, 1),
              xytext=(0, 5),
              xycoords="axes fraction", textcoords="offset points", ha="center"
              )
-ax1.bar(range(1,5), range(1,5), color='gray', edgecolor='black', hatch="/")
+ax1.bar(range(1,5), range(1,5), color='red', edgecolor='black', hatch="/")
+ax1.bar(range(1,5), [6] * 4, bottom=range(1,5), color='blue', 
edgecolor='black', hatch='//')
 
-
 ax2 = fig.add_subplot(122)
-bars = ax2.bar(range(1,5), range(1,5), color='gray', ecolor='black')
+bars = ax2.bar(range(1,5), range(1,5), color='yellow', ecolor='black') + \
+    ax2.bar(range(1, 5), [6] * 4, bottom=range(1,5), color='green', 
ecolor='black')
 
-patterns = ('/', '+', 'x', '\\')
+patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
 for bar, pattern in zip(bars, patterns):
      bar.set_hatch(pattern)
 

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2009-01-06 
17:06:06 UTC (rev 6743)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py     2009-01-06 
17:24:32 UTC (rev 6744)
@@ -557,7 +557,7 @@
             'FirstChar': 0,
             'LastChar':  len(fontinfo.dvifont.widths) - 1,
             'Widths':    widthsObject,
-            }            
+            }
 
         # Encoding (if needed)
         if fontinfo.encodingfile is not None:
@@ -595,7 +595,7 @@
             fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile)
             self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc
         fontdict['FontDescriptor'] = fontdesc
-            
+
         self.writeObject(fontdictObject, fontdict)
         return fontdictObject
 
@@ -1749,7 +1749,6 @@
             else:
                 return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
         else:
-            hatch = hatch.lower()
             hatch_style = (self._rgb, self._fillcolor, hatch)
             name = self.file.hatchPattern(hatch_style)
             return [Name('Pattern'), Op.setcolorspace_nonstroke,

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py     2009-01-06 
17:06:06 UTC (rev 6743)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py     2009-01-06 
17:24:32 UTC (rev 6744)
@@ -95,17 +95,22 @@
         """
         Create a new hatch pattern
         """
-        HATCH_SIZE = 144
-        dictkey = (gc.get_hatch().lower(), rgbFace, gc.get_rgb())
+        HATCH_SIZE = 72
+        dictkey = (gc.get_hatch(), 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_data = self._convert_path(
+                gc.get_hatch_path(),
+                Affine2D().scale(HATCH_SIZE).scale(1.0, -1.0).translate(0, 
HATCH_SIZE))
+            self._svgwriter.write(
+                '<rect x="0" y="0" width="%d" height="%d" fill="%s"/>' %
+                (HATCH_SIZE+1, HATCH_SIZE+1, rgb2hex(rgbFace)))
             path = '<path d="%s" fill="%s" stroke="%s" stroke-width="1.0"/>' % 
(
-                path_data, rgb2hex(rgbFace[:3]), rgb2hex(gc.get_rgb()[:3]))
+                path_data, rgb2hex(gc.get_rgb()[:3]), 
rgb2hex(gc.get_rgb()[:3]))
             self._svgwriter.write(path)
             self._svgwriter.write('\n  </pattern>\n</defs>')
             self._hatchd[dictkey] = id

Added: trunk/matplotlib/lib/matplotlib/hatch.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/hatch.py                            (rev 0)
+++ trunk/matplotlib/lib/matplotlib/hatch.py    2009-01-06 17:24:32 UTC (rev 
6744)
@@ -0,0 +1,194 @@
+"""
+Contains a classes for generating hatch patterns.
+"""
+
+import numpy as np
+from matplotlib.path import Path
+
+class HatchPatternBase:
+    """
+    The base class for a hatch pattern.
+    """
+    pass
+
+class HorizontalHatch(HatchPatternBase):
+    def __init__(self, hatch, density):
+        self.num_lines = (hatch.count('-') + hatch.count('+')) * density
+        self.num_vertices = self.num_lines * 2
+
+    def set_vertices_and_codes(self, vertices, codes):
+        steps = np.linspace(0.0, 1.0, self.num_lines, False)
+        vertices[0::2, 0] = 0.0
+        vertices[0::2, 1] = steps
+        vertices[1::2, 0] = 1.0
+        vertices[1::2, 1] = steps
+        codes[0::2] = Path.MOVETO
+        codes[1::2] = Path.LINETO
+
+class VerticalHatch(HatchPatternBase):
+    def __init__(self, hatch, density):
+        self.num_lines = (hatch.count('|') + hatch.count('+')) * density
+        self.num_vertices = self.num_lines * 2
+
+    def set_vertices_and_codes(self, vertices, codes):
+        steps = np.linspace(0.0, 1.0, self.num_lines, False)
+        vertices[0::2, 0] = steps
+        vertices[0::2, 1] = 0.0
+        vertices[1::2, 0] = steps
+        vertices[1::2, 1] = 1.0
+        codes[0::2] = Path.MOVETO
+        codes[1::2] = Path.LINETO
+
+class NorthEastHatch(HatchPatternBase):
+    def __init__(self, hatch, density):
+        self.num_lines = (hatch.count('/') + hatch.count('x') + 
hatch.count('X')) * density
+        self.num_vertices = self.num_lines * 4
+
+    def set_vertices_and_codes(self, vertices, codes):
+        steps = np.linspace(0.0, 1.0, self.num_lines, False)
+        rev_steps = 1.0 - steps
+        vertices[0::4, 0] = 0.0
+        vertices[0::4, 1] = steps
+        vertices[1::4, 0] = rev_steps
+        vertices[1::4, 1] = 1.0
+        vertices[2::4, 0] = rev_steps
+        vertices[2::4, 1] = 0.0
+        vertices[3::4, 0] = 1.0
+        vertices[3::4, 1] = steps
+        codes[0::2] = Path.MOVETO
+        codes[1::2] = Path.LINETO
+
+class SouthEastHatch(HatchPatternBase):
+    def __init__(self, hatch, density):
+        self.num_lines = (hatch.count('\\') + hatch.count('x') + 
hatch.count('X')) * density
+        self.num_vertices = self.num_lines * 4
+
+    def set_vertices_and_codes(self, vertices, codes):
+        steps = np.linspace(0.0, 1.0, self.num_lines, False)
+        vertices[0::4, 0] = 1.0
+        vertices[0::4, 1] = steps
+        vertices[1::4, 0] = steps
+        vertices[1::4, 1] = 1.0
+        vertices[2::4, 0] = steps
+        vertices[2::4, 1] = 0.0
+        vertices[3::4, 0] = 0.0
+        vertices[3::4, 1] = steps
+        codes[0::2] = Path.MOVETO
+        codes[1::2] = Path.LINETO
+
+class Shapes(HatchPatternBase):
+    filled = False
+    def __init__(self, hatch, density):
+        if self.num_rows == 0:
+            self.num_shapes = 0
+            self.num_vertices = 0
+        else:
+            self.num_shapes = ((self.num_rows / 2 + 1) * (self.num_rows + 1) +
+                               (self.num_rows / 2) * (self.num_rows))
+            self.num_vertices = (self.num_shapes *
+                                 len(self.shape_vertices) *
+                                 (self.filled and 1 or 2))
+
+    def set_vertices_and_codes(self, vertices, codes):
+        offset = 1.0 / self.num_rows
+        shape_vertices = self.shape_vertices * offset * self.size
+        if not self.filled:
+            inner_vertices = shape_vertices[::-1] * 0.9
+        shape_codes = self.shape_codes
+        shape_size = len(shape_vertices)
+
+        cursor = 0
+        for row in xrange(self.num_rows + 1):
+            if row % 2 == 0:
+                cols = np.linspace(0.0, 1.0, self.num_rows + 1, True)
+            else:
+                cols = np.linspace(offset / 2.0, 1.0 - offset / 2.0, 
self.num_rows, True)
+            row_pos = row * offset
+            for col_pos in cols:
+                vertices[cursor:cursor+shape_size] = shape_vertices + 
(col_pos, row_pos)
+                codes[cursor:cursor+shape_size] = shape_codes
+                cursor += shape_size
+                if not self.filled:
+                    vertices[cursor:cursor+shape_size] = inner_vertices + 
(col_pos, row_pos)
+                    codes[cursor:cursor+shape_size] = shape_codes
+                    cursor += shape_size
+
+class Circles(Shapes):
+    def __init__(self, hatch, density):
+        path = Path.unit_circle()
+        self.shape_vertices = path.vertices
+        self.shape_codes = path.codes
+        Shapes.__init__(self, hatch, density)
+
+class SmallCircles(Circles):
+    size = 0.2
+
+    def __init__(self, hatch, density):
+        self.num_rows = (hatch.count('o')) * density
+        Circles.__init__(self, hatch, density)
+
+class LargeCircles(Circles):
+    size = 0.35
+
+    def __init__(self, hatch, density):
+        self.num_rows = (hatch.count('O')) * density
+        Circles.__init__(self, hatch, density)
+
+class SmallFilledCircles(SmallCircles):
+    size = 0.1
+    filled = True
+
+    def __init__(self, hatch, density):
+        self.num_rows = (hatch.count('.')) * density
+        Circles.__init__(self, hatch, density)
+
+class Stars(Shapes):
+    size = 1.0 / 3.0
+    filled = True
+
+    def __init__(self, hatch, density):
+        self.num_rows = (hatch.count('*')) * density
+        path = Path.unit_regular_star(5)
+        self.shape_vertices = path.vertices
+        self.shape_codes = np.ones(len(self.shape_vertices)) * Path.LINETO
+        self.shape_codes[0] = Path.MOVETO
+        Shapes.__init__(self, hatch, density)
+
+_hatch_types = [
+    HorizontalHatch,
+    VerticalHatch,
+    NorthEastHatch,
+    SouthEastHatch,
+    SmallCircles,
+    LargeCircles,
+    SmallFilledCircles,
+    Stars
+    ]
+
+def get_path(hatchpattern, density=6):
+    """
+    Given a hatch specifier, *hatchpattern*, generates Path to render
+    the hatch in a unit square.  *density* is the number of lines per
+    unit square.
+    """
+    size = 1.0
+    density = int(density)
+
+    patterns = [hatch_type(hatchpattern, density) for hatch_type in 
_hatch_types]
+    num_vertices = sum([pattern.num_vertices for pattern in patterns])
+
+    if num_vertices == 0:
+        return Path(np.empty((0, 2)))
+
+    vertices = np.empty((num_vertices, 2))
+    codes    = np.empty((num_vertices,), np.uint8)
+
+    cursor = 0
+    for pattern in patterns:
+        if pattern.num_vertices != 0:
+            vertices_chunk = vertices[cursor:cursor + pattern.num_vertices]
+            codes_chunk = codes[cursor:cursor + pattern.num_vertices]
+            pattern.set_vertices_and_codes(vertices_chunk, codes_chunk)
+            cursor += pattern.num_vertices
+
+    return Path(vertices, codes)

Modified: trunk/matplotlib/lib/matplotlib/patches.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/patches.py  2009-01-06 17:06:06 UTC (rev 
6743)
+++ trunk/matplotlib/lib/matplotlib/patches.py  2009-01-06 17:24:32 UTC (rev 
6744)
@@ -238,19 +238,23 @@
           \   - back diagonal
           |   - vertical
           -   - horizontal
-          #   - crossed
+          +   - crossed
           x   - crossed diagonal
+          o   - small circle
+          O   - large circle
+          .   - dots
+          *   - stars
 
         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.
+        density of hatching of that pattern.
 
         CURRENT LIMITATIONS:
 
         1. Hatching is supported in the PostScript, PDF, SVG and Agg
            backends only.
 
-        ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] (ps & pdf backend only)
+        ACCEPTS: [ '/' | '\\' | '|' | '-' | '+' | 'x' | 'o' | 'O' | '.' | ' *' 
]
         """
         self._hatch = hatch
 

Modified: trunk/matplotlib/lib/matplotlib/path.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/path.py     2009-01-06 17:06:06 UTC (rev 
6743)
+++ trunk/matplotlib/lib/matplotlib/path.py     2009-01-06 17:24:32 UTC (rev 
6744)
@@ -572,87 +572,17 @@
         can be used in a repeated hatching pattern.  *density* is the
         number of lines per unit square.
         """
+        from matplotlib.hatch import get_path
+
         if hatchpattern is None:
             return None
 
-        hatch = hatchpattern.lower()
-        hatch_path = cls._hatch_dict.get((hatch, density))
+        hatch_path = cls._hatch_dict.get((hatchpattern, 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
-
-        # - horizontal
-        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
-
-        # / ne
-        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
-
-        # | vertical
-        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
-
-        # \ se
-        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
+        hatch_path = get_path(hatchpattern, density)
+        cls._hatch_dict[(hatchpattern, density)] = hatch_path
         return hatch_path
     hatch = classmethod(hatch)
 

Modified: trunk/matplotlib/src/_backend_agg.cpp
===================================================================
--- trunk/matplotlib/src/_backend_agg.cpp       2009-01-06 17:06:06 UTC (rev 
6743)
+++ trunk/matplotlib/src/_backend_agg.cpp       2009-01-06 17:24:32 UTC (rev 
6744)
@@ -901,18 +901,20 @@
     // 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;
+    typedef agg::conv_curve<hatch_path_simplify_t> hatch_path_curve_t;
+    typedef agg::conv_stroke<hatch_path_curve_t> hatch_path_stroke_t;
 
     PathIterator hatch_path(gc.hatchpath);
     agg::trans_affine hatch_trans;
+    hatch_trans *= agg::trans_affine_scaling(1.0, -1.0);
+    hatch_trans *= agg::trans_affine_translation(0.0, 1.0);
     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_simplify_t hatch_path_simplify(hatch_path_trans, false, false, 
HATCH_SIZE, HATCH_SIZE);
+    hatch_path_curve_t hatch_path_curve(hatch_path_simplify);
+    hatch_path_stroke_t hatch_path_stroke(hatch_path_curve);
     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);
@@ -920,7 +922,11 @@
     renderer_aa rs(rb);
     rb.clear(agg::rgba(0.0, 0.0, 0.0, 0.0));
     rs.color(gc.color);
+
+    theRasterizer.add_path(hatch_path_curve);
     agg::render_scanlines(theRasterizer, slineP8, rs);
+    theRasterizer.add_path(hatch_path_stroke);
+    agg::render_scanlines(theRasterizer, slineP8, rs);
 
     // Put clipping back on, if originally set on entry to this
     // function

Modified: trunk/matplotlib/src/_backend_agg.h
===================================================================
--- trunk/matplotlib/src/_backend_agg.h 2009-01-06 17:06:06 UTC (rev 6743)
+++ trunk/matplotlib/src/_backend_agg.h 2009-01-06 17:24:32 UTC (rev 6744)
@@ -208,9 +208,7 @@
   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;
+  static const size_t HATCH_SIZE = 72;
   agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4];
   agg::rendering_buffer hatchRenderingBuffer;
 


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

------------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It is the best place to buy or sell services for
just about anything Open Source.
http://p.sf.net/sfu/Xq1LFB
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to