Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ea19fbe

Browse files
committed
Add more hatch styles. Improve consistency across backends.
svn path=/trunk/matplotlib/; revision=6744
1 parent 58e39b4 commit ea19fbe

8 files changed

Lines changed: 235 additions & 98 deletions

File tree

examples/pylab_examples/hatch_demo.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@
66

77
fig = plt.figure()
88
ax1 = fig.add_subplot(121)
9-
ax1.annotate("Hatch is only supported in the PS and PDF backend", (1, 1),
9+
ax1.annotate("Hatch is only supported in the PS, PDF, SVG and Agg backends", (1, 1),
1010
xytext=(0, 5),
1111
xycoords="axes fraction", textcoords="offset points", ha="center"
1212
)
13-
ax1.bar(range(1,5), range(1,5), color='gray', edgecolor='black', hatch="/")
14-
13+
ax1.bar(range(1,5), range(1,5), color='red', edgecolor='black', hatch="/")
14+
ax1.bar(range(1,5), [6] * 4, bottom=range(1,5), color='blue', edgecolor='black', hatch='//')
1515

1616
ax2 = fig.add_subplot(122)
17-
bars = ax2.bar(range(1,5), range(1,5), color='gray', ecolor='black')
17+
bars = ax2.bar(range(1,5), range(1,5), color='yellow', ecolor='black') + \
18+
ax2.bar(range(1, 5), [6] * 4, bottom=range(1,5), color='green', ecolor='black')
1819

19-
patterns = ('/', '+', 'x', '\\')
20+
patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
2021
for bar, pattern in zip(bars, patterns):
2122
bar.set_hatch(pattern)
2223

lib/matplotlib/backends/backend_pdf.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ def embedTeXFont(self, texname, fontinfo):
557557
'FirstChar': 0,
558558
'LastChar': len(fontinfo.dvifont.widths) - 1,
559559
'Widths': widthsObject,
560-
}
560+
}
561561

562562
# Encoding (if needed)
563563
if fontinfo.encodingfile is not None:
@@ -595,7 +595,7 @@ def embedTeXFont(self, texname, fontinfo):
595595
fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile)
596596
self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc
597597
fontdict['FontDescriptor'] = fontdesc
598-
598+
599599
self.writeObject(fontdictObject, fontdict)
600600
return fontdictObject
601601

@@ -1749,7 +1749,6 @@ def hatch_cmd(self, hatch):
17491749
else:
17501750
return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
17511751
else:
1752-
hatch = hatch.lower()
17531752
hatch_style = (self._rgb, self._fillcolor, hatch)
17541753
name = self.file.hatchPattern(hatch_style)
17551754
return [Name('Pattern'), Op.setcolorspace_nonstroke,

lib/matplotlib/backends/backend_svg.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,22 @@ def _get_hatch(self, gc, rgbFace):
9595
"""
9696
Create a new hatch pattern
9797
"""
98-
HATCH_SIZE = 144
99-
dictkey = (gc.get_hatch().lower(), rgbFace, gc.get_rgb())
98+
HATCH_SIZE = 72
99+
dictkey = (gc.get_hatch(), rgbFace, gc.get_rgb())
100100
id = self._hatchd.get(dictkey)
101101
if id is None:
102102
id = 'h%s' % md5(str(dictkey)).hexdigest()
103103
self._svgwriter.write('<defs>\n <pattern id="%s" ' % id)
104104
self._svgwriter.write('patternUnits="userSpaceOnUse" x="0" y="0" ')
105105
self._svgwriter.write(' width="%d" height="%d" >\n' % (HATCH_SIZE, HATCH_SIZE))
106-
path_data = self._convert_path(gc.get_hatch_path(), Affine2D().scale(144))
106+
path_data = self._convert_path(
107+
gc.get_hatch_path(),
108+
Affine2D().scale(HATCH_SIZE).scale(1.0, -1.0).translate(0, HATCH_SIZE))
109+
self._svgwriter.write(
110+
'<rect x="0" y="0" width="%d" height="%d" fill="%s"/>' %
111+
(HATCH_SIZE+1, HATCH_SIZE+1, rgb2hex(rgbFace)))
107112
path = '<path d="%s" fill="%s" stroke="%s" stroke-width="1.0"/>' % (
108-
path_data, rgb2hex(rgbFace[:3]), rgb2hex(gc.get_rgb()[:3]))
113+
path_data, rgb2hex(gc.get_rgb()[:3]), rgb2hex(gc.get_rgb()[:3]))
109114
self._svgwriter.write(path)
110115
self._svgwriter.write('\n </pattern>\n</defs>')
111116
self._hatchd[dictkey] = id

lib/matplotlib/hatch.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
"""
2+
Contains a classes for generating hatch patterns.
3+
"""
4+
5+
import numpy as np
6+
from matplotlib.path import Path
7+
8+
class HatchPatternBase:
9+
"""
10+
The base class for a hatch pattern.
11+
"""
12+
pass
13+
14+
class HorizontalHatch(HatchPatternBase):
15+
def __init__(self, hatch, density):
16+
self.num_lines = (hatch.count('-') + hatch.count('+')) * density
17+
self.num_vertices = self.num_lines * 2
18+
19+
def set_vertices_and_codes(self, vertices, codes):
20+
steps = np.linspace(0.0, 1.0, self.num_lines, False)
21+
vertices[0::2, 0] = 0.0
22+
vertices[0::2, 1] = steps
23+
vertices[1::2, 0] = 1.0
24+
vertices[1::2, 1] = steps
25+
codes[0::2] = Path.MOVETO
26+
codes[1::2] = Path.LINETO
27+
28+
class VerticalHatch(HatchPatternBase):
29+
def __init__(self, hatch, density):
30+
self.num_lines = (hatch.count('|') + hatch.count('+')) * density
31+
self.num_vertices = self.num_lines * 2
32+
33+
def set_vertices_and_codes(self, vertices, codes):
34+
steps = np.linspace(0.0, 1.0, self.num_lines, False)
35+
vertices[0::2, 0] = steps
36+
vertices[0::2, 1] = 0.0
37+
vertices[1::2, 0] = steps
38+
vertices[1::2, 1] = 1.0
39+
codes[0::2] = Path.MOVETO
40+
codes[1::2] = Path.LINETO
41+
42+
class NorthEastHatch(HatchPatternBase):
43+
def __init__(self, hatch, density):
44+
self.num_lines = (hatch.count('/') + hatch.count('x') + hatch.count('X')) * density
45+
self.num_vertices = self.num_lines * 4
46+
47+
def set_vertices_and_codes(self, vertices, codes):
48+
steps = np.linspace(0.0, 1.0, self.num_lines, False)
49+
rev_steps = 1.0 - steps
50+
vertices[0::4, 0] = 0.0
51+
vertices[0::4, 1] = steps
52+
vertices[1::4, 0] = rev_steps
53+
vertices[1::4, 1] = 1.0
54+
vertices[2::4, 0] = rev_steps
55+
vertices[2::4, 1] = 0.0
56+
vertices[3::4, 0] = 1.0
57+
vertices[3::4, 1] = steps
58+
codes[0::2] = Path.MOVETO
59+
codes[1::2] = Path.LINETO
60+
61+
class SouthEastHatch(HatchPatternBase):
62+
def __init__(self, hatch, density):
63+
self.num_lines = (hatch.count('\\') + hatch.count('x') + hatch.count('X')) * density
64+
self.num_vertices = self.num_lines * 4
65+
66+
def set_vertices_and_codes(self, vertices, codes):
67+
steps = np.linspace(0.0, 1.0, self.num_lines, False)
68+
vertices[0::4, 0] = 1.0
69+
vertices[0::4, 1] = steps
70+
vertices[1::4, 0] = steps
71+
vertices[1::4, 1] = 1.0
72+
vertices[2::4, 0] = steps
73+
vertices[2::4, 1] = 0.0
74+
vertices[3::4, 0] = 0.0
75+
vertices[3::4, 1] = steps
76+
codes[0::2] = Path.MOVETO
77+
codes[1::2] = Path.LINETO
78+
79+
class Shapes(HatchPatternBase):
80+
filled = False
81+
def __init__(self, hatch, density):
82+
if self.num_rows == 0:
83+
self.num_shapes = 0
84+
self.num_vertices = 0
85+
else:
86+
self.num_shapes = ((self.num_rows / 2 + 1) * (self.num_rows + 1) +
87+
(self.num_rows / 2) * (self.num_rows))
88+
self.num_vertices = (self.num_shapes *
89+
len(self.shape_vertices) *
90+
(self.filled and 1 or 2))
91+
92+
def set_vertices_and_codes(self, vertices, codes):
93+
offset = 1.0 / self.num_rows
94+
shape_vertices = self.shape_vertices * offset * self.size
95+
if not self.filled:
96+
inner_vertices = shape_vertices[::-1] * 0.9
97+
shape_codes = self.shape_codes
98+
shape_size = len(shape_vertices)
99+
100+
cursor = 0
101+
for row in xrange(self.num_rows + 1):
102+
if row % 2 == 0:
103+
cols = np.linspace(0.0, 1.0, self.num_rows + 1, True)
104+
else:
105+
cols = np.linspace(offset / 2.0, 1.0 - offset / 2.0, self.num_rows, True)
106+
row_pos = row * offset
107+
for col_pos in cols:
108+
vertices[cursor:cursor+shape_size] = shape_vertices + (col_pos, row_pos)
109+
codes[cursor:cursor+shape_size] = shape_codes
110+
cursor += shape_size
111+
if not self.filled:
112+
vertices[cursor:cursor+shape_size] = inner_vertices + (col_pos, row_pos)
113+
codes[cursor:cursor+shape_size] = shape_codes
114+
cursor += shape_size
115+
116+
class Circles(Shapes):
117+
def __init__(self, hatch, density):
118+
path = Path.unit_circle()
119+
self.shape_vertices = path.vertices
120+
self.shape_codes = path.codes
121+
Shapes.__init__(self, hatch, density)
122+
123+
class SmallCircles(Circles):
124+
size = 0.2
125+
126+
def __init__(self, hatch, density):
127+
self.num_rows = (hatch.count('o')) * density
128+
Circles.__init__(self, hatch, density)
129+
130+
class LargeCircles(Circles):
131+
size = 0.35
132+
133+
def __init__(self, hatch, density):
134+
self.num_rows = (hatch.count('O')) * density
135+
Circles.__init__(self, hatch, density)
136+
137+
class SmallFilledCircles(SmallCircles):
138+
size = 0.1
139+
filled = True
140+
141+
def __init__(self, hatch, density):
142+
self.num_rows = (hatch.count('.')) * density
143+
Circles.__init__(self, hatch, density)
144+
145+
class Stars(Shapes):
146+
size = 1.0 / 3.0
147+
filled = True
148+
149+
def __init__(self, hatch, density):
150+
self.num_rows = (hatch.count('*')) * density
151+
path = Path.unit_regular_star(5)
152+
self.shape_vertices = path.vertices
153+
self.shape_codes = np.ones(len(self.shape_vertices)) * Path.LINETO
154+
self.shape_codes[0] = Path.MOVETO
155+
Shapes.__init__(self, hatch, density)
156+
157+
_hatch_types = [
158+
HorizontalHatch,
159+
VerticalHatch,
160+
NorthEastHatch,
161+
SouthEastHatch,
162+
SmallCircles,
163+
LargeCircles,
164+
SmallFilledCircles,
165+
Stars
166+
]
167+
168+
def get_path(hatchpattern, density=6):
169+
"""
170+
Given a hatch specifier, *hatchpattern*, generates Path to render
171+
the hatch in a unit square. *density* is the number of lines per
172+
unit square.
173+
"""
174+
size = 1.0
175+
density = int(density)
176+
177+
patterns = [hatch_type(hatchpattern, density) for hatch_type in _hatch_types]
178+
num_vertices = sum([pattern.num_vertices for pattern in patterns])
179+
180+
if num_vertices == 0:
181+
return Path(np.empty((0, 2)))
182+
183+
vertices = np.empty((num_vertices, 2))
184+
codes = np.empty((num_vertices,), np.uint8)
185+
186+
cursor = 0
187+
for pattern in patterns:
188+
if pattern.num_vertices != 0:
189+
vertices_chunk = vertices[cursor:cursor + pattern.num_vertices]
190+
codes_chunk = codes[cursor:cursor + pattern.num_vertices]
191+
pattern.set_vertices_and_codes(vertices_chunk, codes_chunk)
192+
cursor += pattern.num_vertices
193+
194+
return Path(vertices, codes)

lib/matplotlib/patches.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,19 +238,23 @@ def set_hatch(self, hatch):
238238
\ - back diagonal
239239
| - vertical
240240
- - horizontal
241-
# - crossed
241+
+ - crossed
242242
x - crossed diagonal
243+
o - small circle
244+
O - large circle
245+
. - dots
246+
* - stars
243247
244248
Letters can be combined, in which case all the specified
245249
hatchings are done. If same letter repeats, it increases the
246-
density of hatching in that direction.
250+
density of hatching of that pattern.
247251
248252
CURRENT LIMITATIONS:
249253
250254
1. Hatching is supported in the PostScript, PDF, SVG and Agg
251255
backends only.
252256
253-
ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] (ps & pdf backend only)
257+
ACCEPTS: [ '/' | '\\' | '|' | '-' | '+' | 'x' | 'o' | 'O' | '.' | ' *' ]
254258
"""
255259
self._hatch = hatch
256260

lib/matplotlib/path.py

Lines changed: 5 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -572,87 +572,17 @@ def hatch(cls, hatchpattern, density=6):
572572
can be used in a repeated hatching pattern. *density* is the
573573
number of lines per unit square.
574574
"""
575+
from matplotlib.hatch import get_path
576+
575577
if hatchpattern is None:
576578
return None
577579

578-
hatch = hatchpattern.lower()
579-
hatch_path = cls._hatch_dict.get((hatch, density))
580+
hatch_path = cls._hatch_dict.get((hatchpattern, density))
580581
if hatch_path is not None:
581582
return hatch_path
582583

583-
size = 1.0
584-
density = int(density)
585-
counts = [
586-
hatch.count('-') + hatch.count('+'),
587-
hatch.count('/') + hatch.count('x'),
588-
hatch.count('|') + hatch.count('+'),
589-
hatch.count('\\') + hatch.count('x')
590-
]
591-
592-
if sum(counts) == 0:
593-
return cls([])
594-
595-
counts = [x * density for x in counts]
596-
597-
num_vertices = (counts[0] * 2 + counts[1] * 4 +
598-
counts[2] * 2 + counts[3] * 4)
599-
vertices = np.empty((num_vertices, 2))
600-
codes = np.empty((num_vertices,), cls.code_type)
601-
codes[0::2] = cls.MOVETO
602-
codes[1::2] = cls.LINETO
603-
604-
cursor = 0
605-
606-
# - horizontal
607-
if counts[0]:
608-
vertices_chunk = vertices[cursor:cursor + counts[0] * 2]
609-
cursor += counts[0] * 2
610-
steps = np.linspace(0.0, 1.0, counts[0], False)
611-
vertices_chunk[0::2, 0] = 0.0
612-
vertices_chunk[0::2, 1] = steps
613-
vertices_chunk[1::2, 0] = size
614-
vertices_chunk[1::2, 1] = steps
615-
616-
# / ne
617-
if counts[1]:
618-
vertices_chunk = vertices[cursor:cursor + counts[1] * 4]
619-
cursor += counts[1] * 4
620-
steps = np.linspace(0.0, 1.0, counts[1], False)
621-
vertices_chunk[0::4, 0] = 0.0
622-
vertices_chunk[0::4, 1] = steps
623-
vertices_chunk[1::4, 0] = size - steps
624-
vertices_chunk[1::4, 1] = size
625-
vertices_chunk[2::4, 0] = size - steps
626-
vertices_chunk[2::4, 1] = 0.0
627-
vertices_chunk[3::4, 0] = size
628-
vertices_chunk[3::4, 1] = steps
629-
630-
# | vertical
631-
if counts[2]:
632-
vertices_chunk = vertices[cursor:cursor + counts[2] * 2]
633-
cursor += counts[2] * 2
634-
steps = np.linspace(0.0, 1.0, counts[2], False)
635-
vertices_chunk[0::2, 0] = steps
636-
vertices_chunk[0::2, 1] = 0.0
637-
vertices_chunk[1::2, 0] = steps
638-
vertices_chunk[1::2, 1] = size
639-
640-
# \ se
641-
if counts[3]:
642-
vertices_chunk = vertices[cursor:cursor + counts[3] * 4]
643-
cursor += counts[3] * 4
644-
steps = np.linspace(0.0, 1.0, counts[3], False)
645-
vertices_chunk[0::4, 0] = size
646-
vertices_chunk[0::4, 1] = steps
647-
vertices_chunk[1::4, 0] = steps
648-
vertices_chunk[1::4, 1] = size
649-
vertices_chunk[2::4, 0] = steps
650-
vertices_chunk[2::4, 1] = 0.0
651-
vertices_chunk[3::4, 0] = 0.0
652-
vertices_chunk[3::4, 1] = steps
653-
654-
hatch_path = cls(vertices, codes)
655-
cls._hatch_dict[(hatch, density)] = hatch_path
584+
hatch_path = get_path(hatchpattern, density)
585+
cls._hatch_dict[(hatchpattern, density)] = hatch_path
656586
return hatch_path
657587
hatch = classmethod(hatch)
658588

0 commit comments

Comments
 (0)