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