Skip to content

Commit 105c428

Browse files
committed
fixed an aspect=auto problem with bezier ellipse approx
svn path=/trunk/matplotlib/; revision=3949
1 parent 4675ea2 commit 105c428

File tree

9 files changed

+350
-84
lines changed

9 files changed

+350
-84
lines changed

CHANGELOG

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
2007-10-15 Fixed a bug in patches.Ellipse that was broken for
2+
aspect='auto'. Scale free ellipses now work properly for
3+
equal and auto on Agg and PS, and they fall back on a
4+
polygonal approximation for nonlinear transformations until
5+
we convince oursleves that the spline approximation holds
6+
for nonlinear transformations. Added
7+
unit/ellipse_compare.py to compare spline with vertex
8+
approx for both aspects. JDH
9+
110
2007-10-05 remove generator expressions from texmanager and mpltraits.
211
generator expressions are not supported by python-2.3 - DSD
312

boilerplate.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
# file is pasted into pylab.py. We did try to do this the smart way,
33
# with callable functions and new.function, but could never get the
44
# docstrings right for python2.2. See
5-
# http://groups-beta.google.com/group/comp.lang.python/messages/1b14640f3a4ad3dc,b3d7453af21e5f82,17739e70ac6f710c,9d5291fce29cbbb1,c5b578e4ffc6af28,056ff270daa2f414?thread_id=dcd63ec13096a0f6&mode=thread
6-
5+
# http://groups.google.com/group/comp.lang.python/browse_frm/thread/dcd63ec13096a0f6/1b14640f3a4ad3dc?#1b14640f3a4ad3dc
76

87

98
# note we check for __doc__ is not None since py2exe optimize removes

lib/matplotlib/backends/backend_ps.py

+50-5
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def _get_font_ttf(self, prop):
329329
size = prop.get_size_in_points()
330330
font.set_size(size, 72.0)
331331
return font
332-
332+
333333
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation):
334334
"""
335335
Draw an arc centered at x,y with width and height and angles
@@ -340,7 +340,7 @@ def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation):
340340
"""
341341
ps = '%f %f translate\n%f rotate\n%f %f translate\n%s ellipse' % \
342342
(x, y, rotation, -x, -y, _nums_to_str(angle1, angle2, 0.5*width, 0.5*height, x, y))
343-
self._draw_ps(ps, gc, rgbFace, "arc")
343+
self._draw_ps(ps, gc, None, "arc")
344344

345345
def _rgba(self, im):
346346
return im.as_rgba_str()
@@ -519,8 +519,41 @@ def drawone(x, y):
519519
end += step
520520
if cliprect: write('grestore\n')
521521

522-
def draw_path(self,gc,rgbFace,path,trans):
523-
pass
522+
def draw_path(self, gc, rgbFace, path):
523+
524+
ps_cmd = []
525+
ps_cmd.append('newpath')
526+
527+
while 1:
528+
code, xp, yp = path.vertex()
529+
530+
#print code, xp, yp
531+
532+
if code == agg.path_cmd_stop:
533+
ps_cmd.append('closepath') # Hack, path_cmd_end_poly not found
534+
break
535+
elif code == agg.path_cmd_move_to:
536+
ps_cmd.append('%g %g m' % (xp,yp))
537+
elif code == agg.path_cmd_line_to:
538+
ps_cmd.append('%g %g l' % (xp,yp))
539+
elif code == agg.path_cmd_curve3:
540+
pass
541+
elif code == agg.path_cmd_curve4:
542+
verts = [xp, yp]
543+
verts.extend(path.vertex()[1:])
544+
verts.extend(path.vertex()[1:])
545+
ps_cmd.append('%g %g %g %g %g %g curveto'%tuple(verts))
546+
elif code == agg.path_cmd_end_poly:
547+
ps_cmd.append('closepath')
548+
elif code == agg.path_cmd_mask:
549+
pass
550+
else:
551+
pass
552+
#print code
553+
554+
ps = '\n'.join(ps_cmd)
555+
556+
self._draw_ps(ps, gc, rgbFace, "custom_path")
524557

525558
def draw_lines(self, gc, x, y, transform):
526559
"""
@@ -883,6 +916,7 @@ def _draw_ps(self, ps, gc, rgbFace, command=None):
883916
# local variable eliminates all repeated attribute lookups
884917
write = self._pswriter.write
885918
write('gsave\n')
919+
886920
if debugPS and command:
887921
write("% "+command+"\n")
888922

@@ -916,6 +950,7 @@ def _draw_ps(self, ps, gc, rgbFace, command=None):
916950
if cliprect:
917951
write("grestore\n")
918952
write('grestore\n')
953+
919954
def push_gc(self, gc, store=1):
920955
"""
921956
Push the current onto stack, with the exception of the clip box, which
@@ -1581,5 +1616,15 @@ class FigureManagerPS(FigureManagerBase):
15811616
0 0 1 5 3 roll arc
15821617
setmatrix
15831618
closepath
1584-
} bind def"""
1619+
} bind def""",
1620+
"""/unitcircle {
1621+
newpath
1622+
-1. 0. moveto
1623+
-1.0 0.552284749831 -0.552284749831 1.0 0.0 1.0 curveto
1624+
0.552284749831 1.0 1.0 0.552284749831 1.0 0.0 curveto
1625+
1.0 -0.552284749831 0.552284749831 -1.0 0.0 -1.0 curveto
1626+
-0.552284749831 -1.0 -1.0 -0.552284749831 -1.0 0.0 curveto
1627+
closepath
1628+
} bind def""",
1629+
15851630
]

lib/matplotlib/cbook.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,18 @@ def is_numlike(obj):
230230
except TypeError: return False
231231
else: return True
232232

233-
def to_filehandle(fname):
233+
def to_filehandle(fname, flag='r'):
234234
"""
235235
fname can be a filename or a file handle. Support for gzipped
236-
files is automatic, if the filename ends in .gz
236+
files is automatic, if the filename ends in .gz. flag is a
237+
read/write flag for file
237238
"""
238239
if is_string_like(fname):
239240
if fname.endswith('.gz'):
240241
import gzip
241-
fh = gzip.open(fname)
242+
fh = gzip.open(fname, flag)
242243
else:
243-
fh = file(fname)
244+
fh = file(fname, flag)
244245
elif hasattr(fname, 'seek'):
245246
fh = fname
246247
else:

lib/matplotlib/mlab.py

+162-2
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,8 @@ def prctile(x, p = (0.0, 25.0, 50.0, 75.0, 100.0)):
809809
If p is a scalar, the largest value of x less than or equal
810810
to the p percentage point in the sequence is returned.
811811
"""
812+
813+
812814
x = npy.ravel(x)
813815
x.sort()
814816
Nx = len(x)
@@ -1282,7 +1284,10 @@ def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',',
12821284
converterd, if not None, is a dictionary mapping column number or
12831285
munged column name to a converter function
12841286
1285-
See examples/loadrec.py
1287+
names, if not None, is a list of header names. In this case, no
1288+
header will be read from the file
1289+
1290+
if no rows are found, None is returned See examples/loadrec.py
12861291
"""
12871292

12881293
if converterd is None:
@@ -1291,9 +1296,42 @@ def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',',
12911296
import dateutil.parser
12921297
parsedate = dateutil.parser.parse
12931298

1299+
12941300
fh = cbook.to_filehandle(fname)
1295-
reader = csv.reader(fh, delimiter=delimiter)
12961301

1302+
1303+
class FH:
1304+
"""
1305+
for space delimited files, we want different behavior than
1306+
comma or tab. Generally, we want multiple spaces to be
1307+
treated as a single separator, whereas with comma and tab we
1308+
want multiple commas to return multiple (empty) fields. The
1309+
join/strip trick below effects this
1310+
"""
1311+
def __init__(self, fh):
1312+
self.fh = fh
1313+
1314+
def close(self):
1315+
self.fh.close()
1316+
1317+
def seek(self, arg):
1318+
self.fh.seek(arg)
1319+
1320+
def fix(self, s):
1321+
return ' '.join(s.split())
1322+
1323+
1324+
def next(self):
1325+
return self.fix(self.fh.next())
1326+
1327+
def __iter__(self):
1328+
for line in self.fh:
1329+
yield self.fix(line)
1330+
1331+
if delimiter==' ':
1332+
fh = FH(fh)
1333+
1334+
reader = csv.reader(fh, delimiter=delimiter)
12971335
def process_skiprows(reader):
12981336
if skiprows:
12991337
for i, row in enumerate(reader):
@@ -1388,9 +1426,131 @@ def get_converters(reader):
13881426
rows.append([func(val) for func, val in zip(converters, row)])
13891427
fh.close()
13901428

1429+
if not len(rows):
1430+
return None
13911431
r = npy.rec.fromrecords(rows, names=names)
13921432
return r
13931433

1434+
1435+
def rec2csv(r, fname, delimiter=','):
1436+
"""
1437+
Save the data from numpy record array r into a comma/space/tab
1438+
delimited file. The record array dtype names will be used for
1439+
column headers.
1440+
1441+
1442+
fname - can be a filename or a file handle. Support for gzipped
1443+
files is automatic, if the filename ends in .gz
1444+
"""
1445+
fh = cbook.to_filehandle(fname, 'w')
1446+
writer = csv.writer(fh, delimiter=delimiter)
1447+
header = r.dtype.names
1448+
writer.writerow(header)
1449+
for row in r:
1450+
writer.writerow(map(str, row))
1451+
fh.close()
1452+
1453+
# some record array helpers
1454+
def rec_append_field(rec, name, arr, dtype=None):
1455+
'return a new record array with field name populated with data from array arr'
1456+
arr = npy.asarray(arr)
1457+
if dtype is None:
1458+
dtype = arr.dtype
1459+
newdtype = npy.dtype(rec.dtype.descr + [(name, dtype)])
1460+
newrec = npy.empty(rec.shape, dtype=newdtype)
1461+
for field in rec.dtype.fields:
1462+
newrec[field] = rec[field]
1463+
newrec[name] = arr
1464+
return newrec.view(npy.recarray)
1465+
1466+
1467+
def rec_drop_fields(rec, names):
1468+
'return a new numpy record array with fields in names dropped'
1469+
1470+
names = set(names)
1471+
Nr = len(rec)
1472+
1473+
newdtype = npy.dtype([(name, rec.dtype[name]) for name in rec.dtype.names
1474+
if name not in names])
1475+
1476+
newrec = npy.empty(Nr, dtype=newdtype)
1477+
for field in newdtype.names:
1478+
newrec[field] = rec[field]
1479+
1480+
return newrec.view(npy.recarray)
1481+
1482+
1483+
def rec_join(key, r1, r2):
1484+
"""
1485+
join record arrays r1 and r2 on key; key is a tuple of field
1486+
names. if r1 and r2 have equal values on all the keys in the key
1487+
tuple, then their fields will be merged into a new record array
1488+
containing the union of the fields of r1 and r2
1489+
"""
1490+
1491+
for name in key:
1492+
if name not in r1.dtype.names:
1493+
raise ValueError('r1 does not have key field %s'%name)
1494+
if name not in r2.dtype.names:
1495+
raise ValueError('r2 does not have key field %s'%name)
1496+
1497+
def makekey(row):
1498+
return tuple([row[name] for name in key])
1499+
1500+
1501+
names = list(r1.dtype.names) + [name for name in r2.dtype.names if name not in set(r1.dtype.names)]
1502+
1503+
1504+
1505+
r1d = dict([(makekey(row),i) for i,row in enumerate(r1)])
1506+
r2d = dict([(makekey(row),i) for i,row in enumerate(r2)])
1507+
1508+
r1keys = set(r1d.keys())
1509+
r2keys = set(r2d.keys())
1510+
1511+
keys = r1keys & r2keys
1512+
1513+
r1ind = [r1d[k] for k in keys]
1514+
r2ind = [r2d[k] for k in keys]
1515+
1516+
1517+
r1 = r1[r1ind]
1518+
r2 = r2[r2ind]
1519+
1520+
r2 = rec_drop_fields(r2, r1.dtype.names)
1521+
1522+
1523+
def key_desc(name):
1524+
'if name is a string key, use the larger size of r1 or r2 before merging'
1525+
dt1 = r1.dtype[name]
1526+
if dt1.type != npy.string_:
1527+
return (name, dt1.descr[0][1])
1528+
1529+
dt2 = r1.dtype[name]
1530+
assert dt2==dt1
1531+
if dt1.num>dt2.num:
1532+
return (name, dt1.descr[0][1])
1533+
else:
1534+
return (name, dt2.descr[0][1])
1535+
1536+
1537+
1538+
keydesc = [key_desc(name) for name in key]
1539+
1540+
newdtype = npy.dtype(keydesc +
1541+
[desc for desc in r1.dtype.descr if desc[0] not in key ] +
1542+
[desc for desc in r2.dtype.descr if desc[0] not in key ] )
1543+
1544+
1545+
newrec = npy.empty(len(r1), dtype=newdtype)
1546+
for field in r1.dtype.names:
1547+
newrec[field] = r1[field]
1548+
1549+
for field in r2.dtype.names:
1550+
newrec[field] = r2[field]
1551+
1552+
return newrec.view(npy.recarray)
1553+
13941554
def slopes(x,y):
13951555
"""
13961556
SLOPES calculate the slope y'(x) Given data vectors X and Y SLOPES

0 commit comments

Comments
 (0)