From: <md...@us...> - 2009-01-06 17:24:35
|
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. |