You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(115) |
Aug
(120) |
Sep
(137) |
Oct
(170) |
Nov
(461) |
Dec
(263) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(120) |
Feb
(74) |
Mar
(35) |
Apr
(74) |
May
(245) |
Jun
(356) |
Jul
(240) |
Aug
(115) |
Sep
(78) |
Oct
(225) |
Nov
(98) |
Dec
(271) |
2009 |
Jan
(132) |
Feb
(84) |
Mar
(74) |
Apr
(56) |
May
(90) |
Jun
(79) |
Jul
(83) |
Aug
(296) |
Sep
(214) |
Oct
(76) |
Nov
(82) |
Dec
(66) |
2010 |
Jan
(46) |
Feb
(58) |
Mar
(51) |
Apr
(77) |
May
(58) |
Jun
(126) |
Jul
(128) |
Aug
(64) |
Sep
(50) |
Oct
(44) |
Nov
(48) |
Dec
(54) |
2011 |
Jan
(68) |
Feb
(52) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2018 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
|
|
|
|
|
|
1
|
2
(1) |
3
(4) |
4
(9) |
5
(14) |
6
(8) |
7
(14) |
8
(1) |
9
(2) |
10
(9) |
11
(5) |
12
(11) |
13
(4) |
14
(4) |
15
(1) |
16
|
17
(1) |
18
(2) |
19
(4) |
20
(10) |
21
(3) |
22
(3) |
23
(2) |
24
(8) |
25
(6) |
26
(5) |
27
|
28
(3) |
29
|
30
(3) |
|
|
|
|
|
|
Revision: 3878 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3878&view=rev Author: jswhit Date: 2007年09月23日 06:50:01 -0700 (2007年9月23日) Log Message: ----------- update Modified Paths: -------------- trunk/toolkits/basemap/Changelog Modified: trunk/toolkits/basemap/Changelog =================================================================== --- trunk/toolkits/basemap/Changelog 2007年09月23日 12:34:34 UTC (rev 3877) +++ trunk/toolkits/basemap/Changelog 2007年09月23日 13:50:01 UTC (rev 3878) @@ -1,4 +1,4 @@ -version 0.9.6 (svn revision 3875) +version 0.9.6 (svn revision 3878) * labelling of meridians and parallels now works with very small map regions (less than 0.2 degrees square). * Subregions of the globe may be specified with llcrnrlat, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3877 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3877&view=rev Author: jswhit Date: 2007年09月23日 05:34:34 -0700 (2007年9月23日) Log Message: ----------- changed opendap server (old one no longer works) Modified Paths: -------------- trunk/toolkits/basemap/examples/pnganim.py Modified: trunk/toolkits/basemap/examples/pnganim.py =================================================================== --- trunk/toolkits/basemap/examples/pnganim.py 2007年09月22日 17:27:53 UTC (rev 3876) +++ trunk/toolkits/basemap/examples/pnganim.py 2007年09月23日 12:34:34 UTC (rev 3877) @@ -47,10 +47,10 @@ raise ValueError,'dates must be in same year' # set OpenDAP server URL. -URLbase="http://www.cdc.noaa.gov/cgi-bin/nph-nc/Datasets/ncep.reanalysis/surface/" -URL=URLbase+'slp.'+YYYY+'.nc' -URLu=URLbase+'uwnd.sig995.'+YYYY+'.nc' -URLv=URLbase+'vwnd.sig995.'+YYYY+'.nc' +URLbase="http://nomad3.ncep.noaa.gov:9090/dods/reanalyses/reanalysis-2/6hr/pgb/" +URL=URLbase+'pres' +URLu=URLbase+'wind' +URLv=URLbase+'wind' print URL print URLu print URLv @@ -71,7 +71,8 @@ # put times in YYYYMMDDHH format. dates=[] for t in times: - fdate = hrs_since_day1CE_todate(int(t)) + t = t*24 + fdate = hrs_since_day1CE_todate(int(t)) dates.append(fdate.strftime('%Y%m%d%H')) if YYYYMMDDHH1 not in dates or YYYYMMDDHH2 not in dates: raise ValueError, 'date1 or date2 not a valid date (must be in form YYYYMMDDHH, where HH is 00,06,12 or 18)' @@ -82,13 +83,13 @@ if ntime1 >= ntime2: raise ValueError,'date2 must be greater than date1' # get sea level pressure and 10-m wind data. -slpdata = data['slp'] -udata = datau['uwnd'] -vdata = datav['vwnd'] +slpdata = data['presmsl'] +udata = datau['ugrdprs'] +vdata = datau['vgrdprs'] # mult slp by 0.01 to put in units of millibars. -slpin = 0.01*(slpdata.scale_factor*p.squeeze(slpdata[ntime1:ntime2+1,:,:]) + slpdata.add_offset) -uin = udata.scale_factor*p.squeeze(udata[ntime1:ntime2+1,:,:]) + udata.add_offset -vin = vdata.scale_factor*p.squeeze(vdata[ntime1:ntime2+1,:,:]) + vdata.add_offset +slpin = 0.01*p.squeeze(slpdata[ntime1:ntime2+1,:,:]) +uin = p.squeeze(udata[ntime1:ntime2+1,0,:,:]) +vin = p.squeeze(vdata[ntime1:ntime2+1,0,:,:]) datelabels = dates[ntime1:ntime2+1] # add cyclic points slp = p.zeros((slpin.shape[0],slpin.shape[1],slpin.shape[2]+1),p.Float64) @@ -109,6 +110,12 @@ # make orthographic basemap. m = Basemap(resolution='c',projection='ortho',lat_0=60.,lon_0=-60.) p.ion() # interactive mode on. +uin = p.squeeze(udata[ntime1:ntime2+1,0,:,:]) +vin = p.squeeze(vdata[ntime1:ntime2+1,0,:,:]) +datelabels = dates[ntime1:ntime2+1] +# make orthographic basemap. +m = Basemap(resolution='c',projection='ortho',lat_0=60.,lon_0=-60.) +p.ion() # interactive mode on. # create figure, add axes (leaving room for colorbar on right) fig = p.figure() ax = fig.add_axes([0.1,0.1,0.7,0.7]) @@ -135,12 +142,10 @@ # plot wind vectors on projection grid (looks better). # first, shift grid so it goes from -180 to 180 (instead of 0 to 360 # in longitude). Otherwise, interpolation is messed up. - # also reverse latitudes (since interpolation expects monotonically - # increasing x and y). - ugrid,newlons = shiftgrid(180.,u[nt,::-1,:],longitudes,start=False) - vgrid,newlons = shiftgrid(180.,v[nt,::-1,:],longitudes,start=False) + ugrid,newlons = shiftgrid(180.,u[nt,:,:],longitudes,start=False) + vgrid,newlons = shiftgrid(180.,v[nt,:,:],longitudes,start=False) # transform vectors to projection grid. - urot,vrot,xx,yy = m.transform_vector(ugrid,vgrid,newlons,latitudes[::-1],51,51,returnxy=True,masked=True) + urot,vrot,xx,yy = m.transform_vector(ugrid,vgrid,newlons,latitudes,51,51,returnxy=True,masked=True) # plot wind vectors over map. Q = m.quiver(xx,yy,urot,vrot,scale=500) # make quiver key. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3876 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3876&view=rev Author: jswhit Date: 2007年09月22日 10:27:53 -0700 (2007年9月22日) Log Message: ----------- updated URL Modified Paths: -------------- trunk/toolkits/basemap/examples/fcstmaps.py Modified: trunk/toolkits/basemap/examples/fcstmaps.py =================================================================== --- trunk/toolkits/basemap/examples/fcstmaps.py 2007年09月22日 15:10:00 UTC (rev 3875) +++ trunk/toolkits/basemap/examples/fcstmaps.py 2007年09月22日 17:27:53 UTC (rev 3876) @@ -50,7 +50,7 @@ # set OpenDAP server URL. HH='09' URLbase="http://nomad3.ncep.noaa.gov:9090/dods/sref/sref" -URL=URLbase+YYYYMMDD+"/sref_eta1_"+HH+"z.ctl" +URL=URLbase+YYYYMMDD+"/sref_eta_ctl1_"+HH+"z" print URL+'\n' try: data = client.open(URL) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3875 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3875&view=rev Author: jswhit Date: 2007年09月22日 08:10:00 -0700 (2007年9月22日) Log Message: ----------- version 0.9.6 is finished Modified Paths: -------------- trunk/toolkits/basemap/Changelog Modified: trunk/toolkits/basemap/Changelog =================================================================== --- trunk/toolkits/basemap/Changelog 2007年09月22日 06:48:49 UTC (rev 3874) +++ trunk/toolkits/basemap/Changelog 2007年09月22日 15:10:00 UTC (rev 3875) @@ -1,4 +1,4 @@ -version 0.9.6 (not yet released) +version 0.9.6 (svn revision 3875) * labelling of meridians and parallels now works with very small map regions (less than 0.2 degrees square). * Subregions of the globe may be specified with llcrnrlat, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3874 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3874&view=rev Author: jouni Date: 2007年09月21日 23:48:49 -0700 (2007年9月21日) Log Message: ----------- Replace some generator expressions by list comprehensions for Python 2.3 compatibility Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/dviread.py Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007年09月21日 16:54:32 UTC (rev 3873) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007年09月22日 06:48:49 UTC (rev 3874) @@ -541,10 +541,10 @@ widths[ch] = afmdata.get_width_char(ch, isord=True) except KeyError: pass - not_None = (ch for ch in range(256) - if widths[ch] is not None) - firstchar = not_None.next() - lastchar = max(not_None) + not_None = [ch for ch in range(256) + if widths[ch] is not None] + firstchar = not_None[0] + lastchar = not_None[-1] widths = widths[firstchar:lastchar+1] for i,w in enumerate(widths): if w is None: widths[i] = 0 Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2007年09月21日 16:54:32 UTC (rev 3873) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2007年09月22日 06:48:49 UTC (rev 3874) @@ -350,9 +350,9 @@ def _xxx(self, special): matplotlib.verbose.report( 'Dvi._xxx: encountered special: %s' - % ''.join((32 <= ord(ch) < 127) and ch - or '<%02x>' % ord(ch) - for ch in special), + % ''.join([(32 <= ord(ch) < 127) and ch + or '<%02x>' % ord(ch) + for ch in special]), 'debug') def _fnt_def(self, k, c, s, d, a, l, n): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3873 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3873&view=rev Author: mdboom Date: 2007年09月21日 09:54:32 -0700 (2007年9月21日) Log Message: ----------- Merged revisions 3870-3872 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3871 | dsdale | 2007年09月21日 11:33:18 -0400 (2007年9月21日) | 2 lines changed cbooks reversed to agree with the python builtin ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CHANGELOG branches/transforms/lib/matplotlib/cbook.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3869 + /trunk/matplotlib:1-3872 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2007年09月21日 16:52:50 UTC (rev 3872) +++ branches/transforms/API_CHANGES 2007年09月21日 16:54:32 UTC (rev 3873) @@ -1,3 +1,8 @@ + Changed cbook.reversed so it yields a tuple rather than a + (index, tuple). This agrees with the python reversed builtin, + and cbook only defines reversed if python doesnt provide the + builtin. + Made skiprows=1 the default on csv2rec The gd and paint backends have been deleted. Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2007年09月21日 16:52:50 UTC (rev 3872) +++ branches/transforms/CHANGELOG 2007年09月21日 16:54:32 UTC (rev 3873) @@ -1,3 +1,6 @@ +2007年09月21日 Changed cbook.reversed to yield the same result as the + python reversed builtin - DSD + 2007年09月13日 The usetex support in the pdf backend is more usable now, so I am enabling it. - JKS Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007年09月21日 16:52:50 UTC (rev 3872) +++ branches/transforms/lib/matplotlib/cbook.py 2007年09月21日 16:54:32 UTC (rev 3873) @@ -482,7 +482,7 @@ enumerate() is new in Python 2.3 """ for i in range(len(seq)-1,-1,-1): - yield i, seq[i] + yield seq[i] # use itertools.izip if available, else use python version This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3872 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3872&view=rev Author: mdboom Date: 2007年09月21日 09:52:50 -0700 (2007年9月21日) Log Message: ----------- Further progress on arbitrary transformations -- zooming and panning now works without any log-scale-specific hacks. (Though the underlying model is slightly wrong.) Added graphviz output support for debugging transformation trees. Masked array handling much more robust. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007年09月21日 15:33:18 UTC (rev 3871) +++ branches/transforms/lib/matplotlib/axes.py 2007年09月21日 16:52:50 UTC (rev 3872) @@ -637,10 +637,14 @@ # self.viewLim, self.bbox) self.preDataTransform = mtransforms.BboxTransform( self.viewLim, mtransforms.Bbox.unit()) - self.dataTransform = mtransforms.TestLogTransform() - # self.dataTransform = mtransforms.Affine2D().scale(1.5) +# self.dataTransform = mtransforms.TestPolarTransform() +# self.dataTransform = mtransforms.blended_transform_factory( +# mtransforms.TestLogTransform(), +# mtransforms.Affine2D()) + self.dataTransform = mtransforms.Affine2D() self.transData = self.preDataTransform + self.dataTransform + mtransforms.BboxTransform( mtransforms.Bbox.unit(), self.bbox) + self.transData.make_graphviz(open("trans.dot", "w")) def get_position(self, original=False): @@ -1523,7 +1527,7 @@ 'return the xaxis scale string: log or linear' # MGDTODO # return self.scaled[self.transData.get_funcx().get_type()] - return 'linear' + return 'log' def set_xscale(self, value, basex = 10, subsx=None): """ Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月21日 15:33:18 UTC (rev 3871) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月21日 16:52:50 UTC (rev 3872) @@ -1655,60 +1655,30 @@ #multiple button can get pressed during motion... if self._button_pressed==1: inverse = trans.inverted() - lastx, lasty = inverse.transform_point((lastx, lasty)) - x, y = inverse.transform_point( (event.x, event.y) ) - if a.get_xscale()=='log': - dx=1-lastx/x - else: - dx=x-lastx - if a.get_yscale()=='log': - dy=1-lasty/y - else: - dy=y-lasty - - dx,dy=format_deltas(event,dx,dy) - - if a.get_xscale()=='log': - xmin *= 1-dx - xmax *= 1-dx - else: - xmin -= dx - xmax -= dx - if a.get_yscale()=='log': - ymin *= 1-dy - ymax *= 1-dy - else: - ymin -= dy - ymax -= dy + dx, dy = event.x - lastx, event.y - lasty + dx, dy = format_deltas(event, dx, dy) + delta = npy.array([[dx, dy], [dx, dy]], npy.float_) + bbox = transforms.Bbox(a.bbox.get_points() - delta) + result = bbox.transformed(inverse) elif self._button_pressed==3: try: + inverse = trans.inverted() dx=(lastx-event.x)/float(a.bbox.width) dy=(lasty-event.y)/float(a.bbox.height) - dx,dy=format_deltas(event,dx,dy) - if a.get_aspect() != 'auto': - dx = 0.5*(dx + dy) - dy = dx - alphax = pow(10.0,dx) - alphay = pow(10.0,dy)#use logscaling, avoid singularities and smother scaling... - inverse = trans.inverted() - lastx, lasty = inverse.transform_point( (lastx, lasty) ) - if a.get_xscale()=='log': - xmin = lastx*(xmin/lastx)**alphax - xmax = lastx*(xmax/lastx)**alphax - else: - xmin = lastx+alphax*(xmin-lastx) - xmax = lastx+alphax*(xmax-lastx) - if a.get_yscale()=='log': - ymin = lasty*(ymin/lasty)**alphay - ymax = lasty*(ymax/lasty)**alphay - else: - ymin = lasty+alphay*(ymin-lasty) - ymax = lasty+alphay*(ymax-lasty) + alphax = pow(10.0, dx) + alphay = pow(10.0, dy) + # MGDTODO: Make better use of numpy + lastx, lasty = inverse.transform_point((lastx, lasty)) + xmin = (lastx + alphax * (xmin - lastx)) + xmax = (lastx + alphax * (xmax - lastx)) + ymin = (lasty + alphay * (ymin - lasty)) + ymax = (lasty + alphay * (ymax - lasty)) + result = transforms.Bbox.from_lbrt(xmin, ymin, xmax, ymax) except OverflowError: warnings.warn('Overflow while panning') return - a.set_xlim(xmin, xmax) - a.set_ylim(ymin, ymax) + a.set_xlim(*result.intervalx) + a.set_ylim(*result.intervaly) self.dynamic_update() Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月21日 15:33:18 UTC (rev 3871) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月21日 16:52:50 UTC (rev 3872) @@ -25,6 +25,53 @@ (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN) = range(8) +def unmasked_index_ranges(mask, compressed = True): + ''' + Calculate the good data ranges in a masked 1-D npy.array, based on mask. + + Returns Nx2 npy.array with each row the start and stop indices + for slices of the compressed npy.array corresponding to each of N + uninterrupted runs of unmasked values. + If optional argument compressed is False, it returns the + start and stop indices into the original npy.array, not the + compressed npy.array. + Returns None if there are no unmasked values. + + Example: + + y = ma.array(npy.arange(5), mask = [0,0,1,0,0]) + #ii = unmasked_index_ranges(y.mask()) + ii = unmasked_index_ranges(ma.getmask(y)) + # returns [[0,2,] [2,4,]] + + y.compressed().filled()[ii[1,0]:ii[1,1]] + # returns npy.array [3,4,] + # (The 'filled()' method converts the masked npy.array to a numerix npy.array.) + + #i0, i1 = unmasked_index_ranges(y.mask(), compressed=False) + i0, i1 = unmasked_index_ranges(ma.getmask(y), compressed=False) + # returns [[0,3,] [2,5,]] + + y.filled()[ii[1,0]:ii[1,1]] + # returns npy.array [3,4,] + + ''' + m = npy.concatenate(((1,), mask, (1,))) + indices = npy.arange(len(mask) + 1) + mdif = m[1:] - m[:-1] + i0 = npy.compress(mdif == -1, indices) + i1 = npy.compress(mdif == 1, indices) + assert len(i0) == len(i1) + if len(i1) == 0: + return None + if not compressed: + return npy.concatenate((i0[:, npy.newaxis], i1[:, npy.newaxis]), axis=1) + seglengths = i1 - i0 + breakpoints = npy.cumsum(seglengths) + ic0 = npy.concatenate(((0,), breakpoints[:-1])) + ic1 = breakpoints + return npy.concatenate((ic0[:, npy.newaxis], ic1[:, npy.newaxis]), axis=1) + def segment_hits(cx,cy,x,y,radius): """Determine if any line segments are within radius of a point. Returns the list of line segments that are within that radius. @@ -302,7 +349,7 @@ self._picker = p def get_window_extent(self, renderer): - xy = self.get_transform()(self._xy) + xy = self.get_transform().transform(self._xy) x = xy[:, 0] y = xy[:, 1] @@ -343,9 +390,6 @@ self._yorig = y self.recache() - # MGDTODO: Masked data arrays are broken - _masked_array_to_path_code_mapping = npy.array( - [Path.LINETO, Path.MOVETO, Path.MOVETO], Path.code_type) def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units @@ -363,24 +407,15 @@ if len(x) != len(y): raise RuntimeError('xdata and ydata must be the same length') - self._xy = npy.vstack((npy.asarray(x, npy.float_), - npy.asarray(y, npy.float_))).transpose() + x = x.reshape((len(x), 1)) + y = y.reshape((len(y), 1)) + + self._xy = ma.concatenate((x, y), 1) self._x = self._xy[:, 0] # just a view self._y = self._xy[:, 1] # just a view self._logcache = None - - mx = ma.getmask(x) - my = ma.getmask(y) - mask = ma.mask_or(mx, my) - codes = None - if mask is not ma.nomask: - m = npy.concatenate(((1,), mask, (1,))) - mdif = m[1:] - m[:-1] - mdif = npy.maximum((mdif[:-1] * -2), mask) - codes = npy.take( - self._masked_array_to_path_code_mapping, - mdif) - self._path = Path(self._xy, codes, closed=False) + # Masked arrays are now handled by the Path class itself + self._path = Path(self._xy, closed=False) # MGDTODO: If _draw_steps is removed, remove the following line also self._step_path = None Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月21日 15:33:18 UTC (rev 3871) +++ branches/transforms/lib/matplotlib/path.py 2007年09月21日 16:52:50 UTC (rev 3872) @@ -1,4 +1,5 @@ import numpy as npy +from numpy import ma as ma class Path(object): # Path codes @@ -21,10 +22,8 @@ code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): - vertices = npy.asarray(vertices, npy.float_) - assert vertices.ndim == 2 - assert vertices.shape[1] == 2 - + vertices = ma.asarray(vertices, npy.float_) + if codes is None: if closed: codes = self.LINETO * npy.ones( @@ -41,10 +40,27 @@ assert codes.ndim == 1 assert len(codes) == len(vertices) + # The path being passed in may have masked values. However, + # the backends are not expected to deal with masked arrays, so + # we must remove them from the array (using compressed), and + # add MOVETO commands to the codes array accordingly. + mask = ma.getmask(vertices) + if mask is not ma.nomask: + mask1d = ma.mask_or(mask[:, 0], mask[:, 1]) + vertices = ma.compress(npy.invert(mask1d), vertices, 0) + codes = npy.where(npy.concatenate((mask1d[-1:], mask1d[:-1])), + self.MOVETO, codes) + codes = ma.masked_array(codes, mask=mask1d).compressed() + codes = npy.asarray(codes, self.code_type) + + vertices = npy.asarray(vertices, npy.float_) + + assert vertices.ndim == 2 + assert vertices.shape[1] == 2 + assert codes.ndim == 1 + self._codes = codes self._vertices = vertices - - assert self._codes.ndim == 1 def __repr__(self): return "Path(%s, %s)" % (self.vertices, self.codes) @@ -91,10 +107,11 @@ def unit_regular_polygon(cls, numVertices): path = cls._unit_regular_polygons.get(numVertices) if path is None: - theta = 2*npy.pi/numVertices * npy.arange(numVertices) - # This is to make sure the polygon always "points-up" + theta = 2*npy.pi/numVertices * npy.arange(numVertices).reshape((numVertices, 1)) + # This initial rotation is to make sure the polygon always + # "points-up" theta += npy.pi / 2.0 - verts = npy.vstack((npy.cos(theta), npy.sin(theta))).transpose() + verts = npy.concatenate((npy.cos(theta), npy.sin(theta))) path = Path(verts) cls._unit_regular_polygons[numVertices] = path return path Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月21日 15:33:18 UTC (rev 3871) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月21日 16:52:50 UTC (rev 3872) @@ -5,6 +5,7 @@ """ import numpy as npy +from numpy import ma as ma from numpy.linalg import inv from sets import Set @@ -19,6 +20,7 @@ class TransformNode(object): def __init__(self): self._parents = Set() + self._children = [] def invalidate(self): self._do_invalidation() @@ -33,7 +35,34 @@ getattr(self, child)._parents.add(self) self._children = children + def make_graphviz(self, fobj): + def recurse(root): + fobj.write('%s [label="%s"];\n' % + (hash(root), root.__class__.__name__)) + if isinstance(root, Affine2DBase): + fobj.write('%s [style=filled, color=".7 .7 .9"];\n' % + hash(root)) + elif isinstance(root, BboxBase): + fobj.write('%s [style=filled, color=".9 .9 .7"];\n' % + hash(root)) + for child_name in root._children: + child = getattr(root, child_name) + fobj.write("%s -> %s;\n" % ( + hash(root), + hash(child))) + recurse(child) + fobj.write("digraph G {\n") + recurse(self) + fobj.write("}\n") + + def is_affine(self): + return isinstance(self, Affine2DBase) + + def is_bbox(self): + return isinstance(self, BboxBase) + + class BboxBase(TransformNode): ''' This is the read-only part of a bounding-box @@ -169,12 +198,6 @@ return Bbox(points) from_lbrt = staticmethod(from_lbrt) - def __copy__(self): - return Bbox(self._points.copy()) - - def __deepcopy__(self, memo): - return Bbox(self._points.copy()) - def __cmp__(self, other): # MGDTODO: Totally suboptimal if isinstance(other, Bbox) and (self._points == other._points).all(): @@ -274,6 +297,8 @@ """ Return the Bbox that bounds all bboxes """ + # MGDTODO: There's got to be a way to utilize numpy here + # to make this faster... assert(len(bboxes)) if len(bboxes) == 1: @@ -297,7 +322,7 @@ class TransformedBbox(BboxBase): def __init__(self, bbox, transform): - assert isinstance(bbox, Bbox) + assert bbox.is_bbox() assert isinstance(transform, Transform) BboxBase.__init__(self) @@ -353,9 +378,6 @@ def is_separable(self): return False - def is_affine(self): - return False - class Affine2DBase(Transform): input_dims = 2 @@ -416,9 +438,9 @@ # print "".join(traceback.format_stack()) # print points mtx = self.get_matrix() - points = npy.asarray(points, npy.float_) + points = ma.asarray(points, npy.float_) points = points.transpose() - points = npy.dot(mtx[0:2, 0:2], points) + points = ma.dot(mtx[0:2, 0:2], points) points = points + mtx[0:2, 2:] return points.transpose() @@ -437,9 +459,6 @@ mtx = self.get_matrix() return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 - def is_affine(self): - return True - class Affine2D(Affine2DBase): def __init__(self, matrix = None): @@ -469,12 +488,6 @@ return 0 return -1 - def __copy__(self): - return Affine2D(self._mtx.copy()) - - def __deepcopy__(self, memo): - return Affine2D(self._mtx.copy()) - #@staticmethod def from_values(a, b, c, d, e, f): return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f)) @@ -542,9 +555,31 @@ mtx = self.get_matrix() return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 - def is_affine(self): - return True +class IdentityTransform(Affine2DBase): + """ + A special class that does the identity transform quickly. + """ + _mtx = npy.identity(3) + + def __cmp__(self, other): + if (isinstance(other, Affine2D) and + (other == IDENTITY)): + return 0 + return -1 + + def get_matrix(self): + return _mtx + + def transform(self, points): + return points + + def transform_without_affine(self, points): + return points, self + + def inverted(self): + return self + IDENTITY = Affine2D() class BlendedGenericTransform(Transform): @@ -553,10 +588,9 @@ def __init__(self, x_transform, y_transform): # Here we ask: "Does it blend?" - assert x_transform.is_separable() - assert y_transform.is_separable() - assert x_transform.input_dims == x_transform.output_dims == 2 - assert y_transform.input_dims == y_transform.output_dims == 2 + # MGDTODO: Turn these checks back on + # assert x_transform.is_separable() + # assert y_transform.is_separable() Transform.__init__(self) self._x = x_transform @@ -576,16 +610,18 @@ return self._x(points) if x.input_dims == 2: - x_points = x.transform(points)[:, 0] + x_points = x.transform(points)[:, 0:1] else: x_points = x.transform(points[:, 0]) - + x_points = x_points.reshape((len(x_points), 1)) + if y.input_dims == 2: - y_points = y.transform(points)[:, 1] + y_points = y.transform(points)[:, 1:] else: y_points = y.transform(points[:, 1]) + y_points = y_points.reshape((len(y_points), 1)) - return npy.vstack((x_points, y_points)).transpose() + return ma.concatenate((x_points, y_points), 1) def inverted(self): return BlendedGenericTransform(self._x.inverted(), self._y.inverted()) @@ -598,6 +634,9 @@ def __init__(self, x_transform, y_transform): assert x_transform.is_affine() assert y_transform.is_affine() + # MGDTODO: Turn these checks back on + # assert x_transform.is_separable() + # assert y_transform.is_separable() Transform.__init__(self) self._x = x_transform self._y = y_transform @@ -649,6 +688,8 @@ self._b = b self.set_children(['_a', '_b']) + self.take_shortcut = b.is_affine() + def __repr__(self): return "CompositeGenericTransform(%s, %s)" % (self._a, self._b) __str__ = __repr__ @@ -656,11 +697,15 @@ def transform(self, points): return self._b.transform(self._a.transform(points)) + def transform_without_affine(self, points): + if self.take_shortcut: + return self._a.transform(points), self._b + return self.transform(points), IDENTITY + def inverted(self): return CompositeGenericTransform(self._b.inverted(), self._a.inverted()) def is_separable(self): - return True return self._a.is_separable() and self._b.is_separable() @@ -702,35 +747,81 @@ output_dims = 1 def transform(self, a): - m = npy.ma.masked_where(a < 0, a) + m = ma.masked_where(a < 0, a) return npy.log10(m) class TestLogTransform(Transform): - input_dims = 2 - output_dims = 2 + input_dims = 1 + output_dims = 1 def transform(self, xy): - marray = npy.ma.masked_where(xy <= 0.0, xy * 10.0) - return npy.log10(marray) + marray = ma.masked_where(xy <= 0.0, xy * 10.0) + return (npy.log10(marray) * 0.5) + 0.5 def inverted(self): return TestInvertLogTransform() + def is_separable(self): + return True + class TestInvertLogTransform(Transform): - input_dims = 2 - output_dims = 2 + input_dims = 1 + output_dims = 1 def transform(self, xy): - return npy.power(10, xy) / 10.0 + return ma.power(10, (xy - 0.5) * 2.0) / 10.0 def inverted(self): return TestLogTransform() + def is_separable(self): + return True + + +class TestPolarTransform(Transform): + input_dims = 2 + output_dims = 2 + + def transform(self, xy): + debug = len(xy) > 4 + x = xy[:, 0:1] + y = xy[:, 1:] + x, y = ((y * npy.cos(x)) + 1.0) * 0.5, ((y * npy.sin(x)) + 1.0) * 0.5 + if debug: + print npy.min(xy[:, 0:1]), npy.max(xy[:, 0:1]), npy.min(xy[:, 1:]), npy.max(xy[:, 1:]) + print x.min(), x.max(), y.min(), y.max() + return ma.concatenate((x, y), 1) + + def inverted(self): + return TestInvertPolarTransform() + def is_separable(self): + return False + + +class TestInvertPolarTransform(Transform): + input_dims = 2 + output_dims = 2 + + def transform(self, xy): + x = xy[:, 0:1] + y = xy[:, 1:] + r = ma.sqrt(ma.power(x, 2) + ma.power(y, 2)) + theta = ma.arccos(x / r) + theta = ma.where(y < 0, 2 * npy.pi - theta, theta) + return ma.concatenate((theta / (npy.pi * 2), r), 1) + + def inverted(self): + return TestInvertPolarTransform() + + def is_separable(self): + return False + + class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): - assert isinstance(boxin, BboxBase) - assert isinstance(boxout, BboxBase) + assert boxin.is_bbox() + assert boxout.is_bbox() Affine2DBase.__init__(self) self._boxin = boxin This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3871 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3871&view=rev Author: dsdale Date: 2007年09月21日 08:33:18 -0700 (2007年9月21日) Log Message: ----------- changed cbooks reversed to agree with the python builtin Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/cbook.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2007年09月20日 18:02:51 UTC (rev 3870) +++ trunk/matplotlib/API_CHANGES 2007年09月21日 15:33:18 UTC (rev 3871) @@ -1,3 +1,8 @@ + Changed cbook.reversed so it yields a tuple rather than a + (index, tuple). This agrees with the python reversed builtin, + and cbook only defines reversed if python doesnt provide the + builtin. + Made skiprows=1 the default on csv2rec The gd and paint backends have been deleted. Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007年09月20日 18:02:51 UTC (rev 3870) +++ trunk/matplotlib/CHANGELOG 2007年09月21日 15:33:18 UTC (rev 3871) @@ -1,3 +1,6 @@ +2007年09月21日 Changed cbook.reversed to yield the same result as the + python reversed builtin - DSD + 2007年09月13日 The usetex support in the pdf backend is more usable now, so I am enabling it. - JKS Modified: trunk/matplotlib/lib/matplotlib/cbook.py =================================================================== --- trunk/matplotlib/lib/matplotlib/cbook.py 2007年09月20日 18:02:51 UTC (rev 3870) +++ trunk/matplotlib/lib/matplotlib/cbook.py 2007年09月21日 15:33:18 UTC (rev 3871) @@ -482,7 +482,7 @@ enumerate() is new in Python 2.3 """ for i in range(len(seq)-1,-1,-1): - yield i, seq[i] + yield seq[i] # use itertools.izip if available, else use python version This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3870 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3870&view=rev Author: mdboom Date: 2007年09月20日 11:02:51 -0700 (2007年9月20日) Log Message: ----------- Merged revisions 3866-3869 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3867 | jdh2358 | 2007年09月20日 10:13:51 -0400 (2007年9月20日) | 1 line committed rectangle selector lock patch ........ Modified Paths: -------------- branches/transforms/lib/matplotlib/mlab.py branches/transforms/lib/matplotlib/widgets.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3865 + /trunk/matplotlib:1-3869 Modified: branches/transforms/lib/matplotlib/mlab.py =================================================================== --- branches/transforms/lib/matplotlib/mlab.py 2007年09月20日 18:00:32 UTC (rev 3869) +++ branches/transforms/lib/matplotlib/mlab.py 2007年09月20日 18:02:51 UTC (rev 3870) @@ -1255,7 +1255,7 @@ if unpack: return X.transpose() else: return X -def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',', +def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',', converterd=None, names=None, missing=None): """ Load data from comma/space/tab delimited file in fname into a @@ -1314,6 +1314,14 @@ else: return get_func(item, funcmap[func]) # recurse else: return func + + # map column names that clash with builtins -- TODO - extend this list + itemd = { + 'return' : 'return_', + 'file' : 'file_', + 'print' : 'print_', + } + def get_converters(reader): converters = None @@ -1352,6 +1360,7 @@ if not len(item): item = 'column%d'%i + item = itemd.get(item, item) cnt = seen.get(item, 0) if cnt>0: names.append(item + '%d'%cnt) Modified: branches/transforms/lib/matplotlib/widgets.py =================================================================== --- branches/transforms/lib/matplotlib/widgets.py 2007年09月20日 18:00:32 UTC (rev 3869) +++ branches/transforms/lib/matplotlib/widgets.py 2007年09月20日 18:02:51 UTC (rev 3870) @@ -955,24 +955,40 @@ warnings.warn('Use SpanSelector instead!', DeprecationWarning) SpanSelector.__init__(self, ax, onselect, 'horizontal', **kwargs) + class RectangleSelector: """ Select a min/max range of the x axes for a matplotlib Axes Example usage: - ax = subplot(111) - ax.plot(x,y) + from matplotlib.widgets import RectangleSelector + from pylab import * - def onselect(eclick, erelease): + def onselect(eclick, erelease): 'eclick and erelease are matplotlib events at press and release' - print 'startposition : (%f,%f)'%(eclick.xdata, eclick.ydata) - print 'endposition : (%f,%f)'%(erelease.xdata, erelease.ydata) - print 'used button : ', eclick.button + print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) + print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) + print ' used button : ', eclick.button - span = Selector(ax, onselect,drawtype='box') - show() + def toggle_Selector(event): + print ' Key pressed.' + if event.key in ['Q', 'q'] and toggle_Selector.RS.active: + print ' RectangleSelector deactivated.' + toggle_Selector.RS.set_active(False) + if event.key in ['A', 'a'] and not toggle_Selector.RS.active: + print ' RectangleSelector activated.' + toggle_Selector.RS.set_active(True) + x = arange(100)/(99.0) + y = sin(x) + fig = figure + ax = subplot(111) + ax.plot(x,y) + + toggle_Selector.RS = RectangleSelector(ax, onselect, drawtype='line') + connect('key_press_event', toggle_Selector) + show() """ def __init__(self, ax, onselect, drawtype='box', minspanx=None, minspany=None, useblit=False, @@ -1001,8 +1017,6 @@ Use type if you want the mouse to draw a line, a box or nothing between click and actual position ny setting drawtype = 'line', drawtype='box' or drawtype = 'none'. - - """ self.ax = ax self.visible = True @@ -1012,6 +1026,7 @@ self.canvas.mpl_connect('button_release_event', self.release) self.canvas.mpl_connect('draw_event', self.update_background) + self.active = True # for activation / deactivation self.to_draw = None self.background = None @@ -1052,6 +1067,14 @@ def ignore(self, event): 'return True if event should be ignored' + # If RectangleSelector is not active : + if not self.active: + return True + + # If canvas was locked + if not self.canvas.widgetlock.available(self): + return True + # If no button was pressed yet ignore the event if it was out # of the axes if self.eventpress == None: @@ -1142,6 +1165,17 @@ self.update() return False + def set_active(self, active): + """ Use this to activate / deactivate the RectangleSelector + + from your program with an boolean variable 'active'. + """ + self.active = active + + def get_active(self): + """ to get status of active mode (boolean variable)""" + return self.active + class Lasso(Widget): def __init__(self, ax, xy, callback=None, useblit=True): self.axes = ax This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3869 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3869&view=rev Author: mdboom Date: 2007年09月20日 11:00:32 -0700 (2007年9月20日) Log Message: ----------- First baby step in getting arbitrary non-linear transformations into the pipeline. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/axes.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -633,10 +633,16 @@ self.transAxes = mtransforms.BboxTransform( mtransforms.Bbox.unit(), self.bbox) # self.set_transform(self.transAxes) - self.transData = mtransforms.BboxTransform( - self.viewLim, self.bbox) +# self.transData = mtransforms.BboxTransform( +# self.viewLim, self.bbox) + self.preDataTransform = mtransforms.BboxTransform( + self.viewLim, mtransforms.Bbox.unit()) + self.dataTransform = mtransforms.TestLogTransform() + # self.dataTransform = mtransforms.Affine2D().scale(1.5) + self.transData = self.preDataTransform + self.dataTransform + mtransforms.BboxTransform( + mtransforms.Bbox.unit(), self.bbox) + - def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' if original: Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/axis.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -1032,18 +1032,17 @@ else: bbox = Bbox.union(bboxes) bottom = bbox.ymin - - self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0)) - + self.label.set_position( (x, bottom - self.LABELPAD*self.figure.dpi / 72.0)) + else: if not len(bboxes2): top = self.axes.bbox.ymax else: bbox = bbox_union(bboxes2) top = bbox.ymax + + self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi / 72.0)) - self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi.get()/72.0)) - def _update_offset_text_position(self, bboxes, bboxes2): """ Update the offset_text position based on the sequence of bounding Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -84,7 +84,8 @@ from matplotlib.font_manager import findfont from matplotlib.ft2font import FT2Font, LOAD_DEFAULT from matplotlib.mathtext import MathTextParser -from matplotlib.transforms import Bbox +from matplotlib.path import Path +from matplotlib.transforms import Affine2D, Bbox from _backend_agg import RendererAgg as _RendererAgg @@ -117,8 +118,8 @@ debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', 'debug-annoying') - self.draw_path = self._renderer.draw_path - self.draw_markers = self._renderer.draw_markers + #self.draw_path = self._renderer.draw_path + #self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region @@ -129,6 +130,17 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') + # MGDTODO: This is a hack for now to allow for arbitrary transformations + def draw_path(self, gc, path, trans, rgbFace=None): + new_path, affine = path.transformed_without_affine(trans) + self._renderer.draw_path(gc, new_path, affine, rgbFace) + + # MGDTODO: This is a hack for now to allow for arbitrary transformations + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + assert marker_trans.is_affine() + new_path, affine = path.transformed_without_affine(trans) + self._renderer.draw_markers(gc, marker_path, marker_trans, new_path, affine, rgbFace) + def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -1102,12 +1102,14 @@ """ return self._dashcapstyle + def get_solid_capstyle(self): """ Get the cap style for solid linestyles """ return self._solidcapstyle + def is_dashed(self): 'return True if line is dashstyle' return self._linestyle in ('--', '-.', ':') Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/path.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -70,6 +70,13 @@ yield vertices[i] i += 1 + def transformed(self, transform): + return Path(transform.transform(self.vertices), self.codes) + + def transformed_without_affine(self, transform): + vertices, affine = transform.transform_without_affine(self.vertices) + return Path(vertices, self.codes), affine + _unit_rectangle = None #@classmethod def unit_rectangle(cls): Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/text.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -185,7 +185,8 @@ xmin, ymin = thisx, thisy lines = self._text.split('\n') - + + # MGDTODO: whs could be a numpy.array whs = [] # Find full vertical extent of font, # including ascenders and descenders: @@ -260,27 +261,21 @@ else: offsety = ty - ymin xmin += offsetx - xmax += offsetx ymin += offsety - ymax += offsety bbox = Bbox.from_lbwh(xmin, ymin, width, height) - - # now rotate the positions around the first x,y position xys = M.transform(offsetLayout) - tx = xys[:, 0] - ty = xys[:, 1] - tx += offsetx - ty += offsety + xys[:, 0] += offsetx + xys[:, 1] += offsety # now inverse transform back to data coords inverse_transform = self.get_transform().inverted() xys = inverse_transform.transform(xys) - xs, ys = zip(*xys) - + xs, ys = xys[:, 0], xys[:, 1] + ret = bbox, zip(lines, whs, xs, ys) self.cached[key] = ret return ret @@ -331,15 +326,15 @@ for line, wh, x, y in info: x, y = trans.transform_point((x, y)) - + if renderer.flipy(): canvasw, canvash = renderer.get_canvas_width_height() y = canvash-y - + renderer.draw_text(gc, x, y, line, self._fontproperties, angle, ismath=self.is_math_text(line)) - + def get_color(self): "Return the color of the text" return self._color @@ -407,7 +402,9 @@ return (x, y, self._text, self._color, self._verticalalignment, self._horizontalalignment, hash(self._fontproperties), self._rotation, - self.get_transform(), + # MGDTODO: Find a better way to determine if the + # transform as changed + str(self.get_transform()) ) def get_text(self): Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月20日 14:26:27 UTC (rev 3868) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月20日 18:00:32 UTC (rev 3869) @@ -256,10 +256,10 @@ self.invalidate() def transformed(self, transform): - return Bbox(transform(self._points)) + return Bbox(transform.transform(self._points)) def inverse_transformed(self, transform): - return Bbox(transform.inverted()(self._points)) + return Bbox(transform.inverted().transform(self._points)) def expanded(self, sw, sh): width = self.width @@ -408,13 +408,13 @@ # MGDTODO: The major speed trap here is just converting to # the points to an array in the first place. If we can use # more arrays upstream, that should help here. - if not isinstance(points, npy.ndarray): - import traceback - print '-' * 60 - print 'A non-numpy array was passed in for transformation. Please ' - print 'correct this.' - print "".join(traceback.format_stack()) - print points +# if not isinstance(points, npy.ndarray): +# import traceback +# print '-' * 60 +# print 'A non-numpy array was passed in for transformation. Please ' +# print 'correct this.' +# print "".join(traceback.format_stack()) +# print points mtx = self.get_matrix() points = npy.asarray(points, npy.float_) points = points.transpose() @@ -563,6 +563,10 @@ self._y = y_transform self.set_children(['_x', '_y']) + def __repr__(self): + return "BlendedGenericTransform(%s,%s)" % (self._x, self._y) + __str__ = __repr__ + def transform(self, points): # MGDTODO: Optimize the case where one of these is # an affine @@ -590,28 +594,6 @@ return True -class BlendedSeparableTransform(Transform): - input_dims = 2 - output_dims = 2 - - def __init__(self, x_transform, y_transform): - # Here we ask: "Does it blend?" - assert x_transform.is_separable() - assert y_transform.is_separable() - assert x_transform.input_dims == x.transform.output_dims == 1 - assert y_transform.input_dims == y.transform.output_dims == 1 - - Transform.__init__(self) - self._x = x_transform - self._y = y_transform - self.set_children(['_x', '_y']) - - def transform(self, points): - x_points = self._x(points[:, 0]) - y_points = self._y(points[:, 1]) - return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose() - - class BlendedAffine2D(Affine2DBase, Transform): def __init__(self, x_transform, y_transform): assert x_transform.is_affine() @@ -666,6 +648,10 @@ self._a = a self._b = b self.set_children(['_a', '_b']) + + def __repr__(self): + return "CompositeGenericTransform(%s, %s)" % (self._a, self._b) + __str__ = __repr__ def transform(self, points): return self._b.transform(self._a.transform(points)) @@ -724,10 +710,21 @@ input_dims = 2 output_dims = 2 def transform(self, xy): - return xy * 2 + marray = npy.ma.masked_where(xy <= 0.0, xy * 10.0) + return npy.log10(marray) + + def inverted(self): + return TestInvertLogTransform() + +class TestInvertLogTransform(Transform): + input_dims = 2 + output_dims = 2 + def transform(self, xy): + return npy.power(10, xy) / 10.0 + def inverted(self): - return self + return TestLogTransform() class BboxTransform(Affine2DBase): @@ -825,7 +822,7 @@ assert bbox.bounds == (10, 15, 10, 10) - print npy.asarray(bbox) + assert tuple(npy.asarray(bbox).flatten()) == (10, 15, 20, 25) bbox.intervalx = (11, 21) bbox.intervaly = (16, 26) @@ -859,29 +856,35 @@ scale = Affine2D().scale(10, 20) assert scale.to_values() == (10, 0, 0, 20, 0, 0) rotation = Affine2D().rotate_deg(30) - print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, + assert rotation.to_values() == (0.86602540378443871, 0.49999999999999994, -0.49999999999999994, 0.86602540378443871, 0.0, 0.0) points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_) - translated_points = translation(points) + translated_points = translation.transform(points) assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all() - scaled_points = scale(points) + scaled_points = scale.transform(points) print scaled_points - rotated_points = rotation(points) + rotated_points = rotation.transform(points) print rotated_points - tpoints1 = rotation(translation(scale(points))) + tpoints1 = rotation.transform(translation.transform(scale.transform(points))) trans_sum = scale + translation + rotation - tpoints2 = trans_sum(points) - print tpoints1, tpoints2 - print tpoints1 == tpoints2 + tpoints2 = trans_sum.transform(points) # Need to do some sort of fuzzy comparison here? - # assert (tpoints1 == tpoints2).all() + assert (tpoints1.round() == tpoints2.round()).all() + print points + + comp = TestLogTransform() + Affine2D().rotate_deg(15) + tpoints = comp.transform(points) + itpoints = comp.inverted().transform(tpoints) + print tpoints, itpoints + assert (points.round() == itpoints.round()).all() + # Here are some timing tests points = npy.asarray([(random(), random()) for i in xrange(10000)]) - t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points") + t = timeit.Timer("trans_sum.transform(points)", "from __main__ import trans_sum, points") print "Time to transform 10000 x 10 points:", t.timeit(10) __all__ = ['Transform', 'Affine2D'] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3868 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3868&view=rev Author: mdboom Date: 2007年09月20日 07:26:27 -0700 (2007年9月20日) Log Message: ----------- Don't copy path array to a contiguous one. Modified Paths: -------------- branches/transforms/src/_backend_agg.cpp Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月20日 14:13:51 UTC (rev 3867) +++ branches/transforms/src/_backend_agg.cpp 2007年09月20日 14:26:27 UTC (rev 3868) @@ -100,17 +100,18 @@ Py::Object vertices_obj = path_obj.getAttr("vertices"); Py::Object codes_obj = path_obj.getAttr("codes"); - vertices = (PyArrayObject*)PyArray_ContiguousFromObject + vertices = (PyArrayObject*)PyArray_FromObject (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) throw Py::ValueError("Invalid vertices array."); - codes = (PyArrayObject*)PyArray_ContiguousFromObject + + codes = (PyArrayObject*)PyArray_FromObject (codes_obj.ptr(), PyArray_UINT8, 1, 1); if (!codes) throw Py::ValueError("Invalid codes array."); if (codes->dimensions[0] != vertices->dimensions[0]) - throw Py::ValueError("Vertices and codes array are not the same length."); + throw Py::ValueError("vertices and codes arrays are not the same length."); m_total_vertices = codes->dimensions[0]; } @@ -125,10 +126,9 @@ inline unsigned vertex(unsigned idx, double* x, double* y) { if (idx > m_total_vertices) throw Py::RuntimeError("Requested vertex past end"); - double* pv = (double*)(vertices->data + (idx * vertices->strides[0])); - *x = *pv++; - *y = *pv; - return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))]; + *x = *(double*)PyArray_GETPTR2(vertices, idx, 0); + *y = *(double*)PyArray_GETPTR2(vertices, idx, 1); + return code_map[(int)*(char *)PyArray_GETPTR1(codes, idx)]; } inline unsigned vertex(double* x, double* y) { @@ -145,12 +145,14 @@ } }; -const char PathIterator::code_map[] = {0, - agg::path_cmd_move_to, - agg::path_cmd_line_to, - agg::path_cmd_curve3, - agg::path_cmd_curve4, - agg::path_cmd_end_poly | agg::path_flags_close}; +// Maps path codes on the Python side to agg path commands +const char PathIterator::code_map[] = + {0, + agg::path_cmd_move_to, + agg::path_cmd_line_to, + agg::path_cmd_curve3, + agg::path_cmd_curve4, + agg::path_cmd_end_poly | agg::path_flags_close}; template<class VertexSource> class conv_quantize { @@ -160,19 +162,16 @@ void set_source(VertexSource& source) { m_source = &source; } - void rewind(unsigned path_id) - { + void rewind(unsigned path_id) { m_source->rewind(path_id); } - unsigned vertex(double* x, double* y) - { + unsigned vertex(double* x, double* y) { unsigned cmd = m_source->vertex(x, y); - if(m_quantize && agg::is_vertex(cmd)) - { - *x = (int)(*x); - *y = (int)(*y); - } + if (m_quantize && agg::is_vertex(cmd)) { + *x = (int)(*x + 0.5); + *y = (int)(*y + 0.5); + } return cmd; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3867 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3867&view=rev Author: jdh2358 Date: 2007年09月20日 07:13:51 -0700 (2007年9月20日) Log Message: ----------- committed rectangle selector lock patch Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/mlab.py trunk/matplotlib/lib/matplotlib/widgets.py Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2007年09月20日 13:59:15 UTC (rev 3866) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2007年09月20日 14:13:51 UTC (rev 3867) @@ -1255,7 +1255,7 @@ if unpack: return X.transpose() else: return X -def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',', +def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',', converterd=None, names=None, missing=None): """ Load data from comma/space/tab delimited file in fname into a @@ -1314,6 +1314,14 @@ else: return get_func(item, funcmap[func]) # recurse else: return func + + # map column names that clash with builtins -- TODO - extend this list + itemd = { + 'return' : 'return_', + 'file' : 'file_', + 'print' : 'print_', + } + def get_converters(reader): converters = None @@ -1352,6 +1360,7 @@ if not len(item): item = 'column%d'%i + item = itemd.get(item, item) cnt = seen.get(item, 0) if cnt>0: names.append(item + '%d'%cnt) Modified: trunk/matplotlib/lib/matplotlib/widgets.py =================================================================== --- trunk/matplotlib/lib/matplotlib/widgets.py 2007年09月20日 13:59:15 UTC (rev 3866) +++ trunk/matplotlib/lib/matplotlib/widgets.py 2007年09月20日 14:13:51 UTC (rev 3867) @@ -955,24 +955,40 @@ warnings.warn('Use SpanSelector instead!', DeprecationWarning) SpanSelector.__init__(self, ax, onselect, 'horizontal', **kwargs) + class RectangleSelector: """ Select a min/max range of the x axes for a matplotlib Axes Example usage: - ax = subplot(111) - ax.plot(x,y) + from matplotlib.widgets import RectangleSelector + from pylab import * - def onselect(eclick, erelease): + def onselect(eclick, erelease): 'eclick and erelease are matplotlib events at press and release' - print 'startposition : (%f,%f)'%(eclick.xdata, eclick.ydata) - print 'endposition : (%f,%f)'%(erelease.xdata, erelease.ydata) - print 'used button : ', eclick.button + print ' startposition : (%f, %f)' % (eclick.xdata, eclick.ydata) + print ' endposition : (%f, %f)' % (erelease.xdata, erelease.ydata) + print ' used button : ', eclick.button - span = Selector(ax, onselect,drawtype='box') - show() + def toggle_Selector(event): + print ' Key pressed.' + if event.key in ['Q', 'q'] and toggle_Selector.RS.active: + print ' RectangleSelector deactivated.' + toggle_Selector.RS.set_active(False) + if event.key in ['A', 'a'] and not toggle_Selector.RS.active: + print ' RectangleSelector activated.' + toggle_Selector.RS.set_active(True) + x = arange(100)/(99.0) + y = sin(x) + fig = figure + ax = subplot(111) + ax.plot(x,y) + + toggle_Selector.RS = RectangleSelector(ax, onselect, drawtype='line') + connect('key_press_event', toggle_Selector) + show() """ def __init__(self, ax, onselect, drawtype='box', minspanx=None, minspany=None, useblit=False, @@ -1001,8 +1017,6 @@ Use type if you want the mouse to draw a line, a box or nothing between click and actual position ny setting drawtype = 'line', drawtype='box' or drawtype = 'none'. - - """ self.ax = ax self.visible = True @@ -1012,6 +1026,7 @@ self.canvas.mpl_connect('button_release_event', self.release) self.canvas.mpl_connect('draw_event', self.update_background) + self.active = True # for activation / deactivation self.to_draw = None self.background = None @@ -1052,6 +1067,14 @@ def ignore(self, event): 'return True if event should be ignored' + # If RectangleSelector is not active : + if not self.active: + return True + + # If canvas was locked + if not self.canvas.widgetlock.available(self): + return True + # If no button was pressed yet ignore the event if it was out # of the axes if self.eventpress == None: @@ -1142,6 +1165,17 @@ self.update() return False + def set_active(self, active): + """ Use this to activate / deactivate the RectangleSelector + + from your program with an boolean variable 'active'. + """ + self.active = active + + def get_active(self): + """ to get status of active mode (boolean variable)""" + return self.active + class Lasso(Widget): def __init__(self, ax, xy, callback=None, useblit=True): self.axes = ax This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3866 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3866&view=rev Author: mdboom Date: 2007年09月20日 06:59:15 -0700 (2007年9月20日) Log Message: ----------- Merged revisions 3847-3865 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3853 | jouni | 2007年09月15日 00:01:56 -0400 (2007年9月15日) | 2 lines Bugfix and doc fixes in type1font.py ........ r3861 | mdboom | 2007年09月20日 08:31:26 -0400 (2007年9月20日) | 2 lines Fix font.size from being saved in the fontManager.cache ........ r3862 | mdboom | 2007年09月20日 08:40:41 -0400 (2007年9月20日) | 2 lines Removing debugging output in last commit. ........ r3863 | jdh2358 | 2007年09月20日 09:50:27 -0400 (2007年9月20日) | 1 line added gradient bar example ........ Modified Paths: -------------- branches/transforms/lib/matplotlib/font_manager.py Added Paths: ----------- branches/transforms/examples/gradient_bar.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3846 + /trunk/matplotlib:1-3865 Copied: branches/transforms/examples/gradient_bar.py (from rev 3863, trunk/matplotlib/examples/gradient_bar.py) =================================================================== --- branches/transforms/examples/gradient_bar.py (rev 0) +++ branches/transforms/examples/gradient_bar.py 2007年09月20日 13:59:15 UTC (rev 3866) @@ -0,0 +1,26 @@ +from pylab import figure, show, nx, cm + +def gbar(ax, x, y, width=0.5, bottom=0): + X = [[.6, .6],[.7,.7]] + for left,top in zip(x, y): + right = left+width + ax.imshow(X, interpolation='bicubic', cmap=cm.Blues, + extent=(left, right, bottom, top), alpha=1) + +fig = figure() + +xmin, xmax = xlim = 0,10 +ymin, ymax = ylim = 0,1 +ax = fig.add_subplot(111, xlim=xlim, ylim=ylim, + autoscale_on=False) +X = [[.6, .6],[.7,.7]] + +ax.imshow(X, interpolation='bicubic', cmap=cm.copper, + extent=(xmin, xmax, ymin, ymax), alpha=1) + +N = 10 +x = nx.arange(N)+0.25 +y = nx.mlab.rand(N) +gbar(ax, x, y, width=0.7) +ax.set_aspect('normal') +show() Modified: branches/transforms/lib/matplotlib/font_manager.py =================================================================== --- branches/transforms/lib/matplotlib/font_manager.py 2007年09月20日 13:57:59 UTC (rev 3865) +++ branches/transforms/lib/matplotlib/font_manager.py 2007年09月20日 13:59:15 UTC (rev 3866) @@ -843,10 +843,9 @@ """ def __init__(self, size=None, weight='normal'): - if not size : size = rcParams['font.size'] - self.__default_size = size self.__default_weight = weight - + self.default_size = size + paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), os.path.join(rcParams['datapath'],'fonts','afm')] @@ -899,7 +898,9 @@ def get_default_size(self): "Return the default font size." - return self.__default_size + if self.default_size is None: + return rcParams['font.size'] + return self.default_size def set_default_weight(self, weight): "Set the default font weight. The initial value is 'normal'." @@ -1085,6 +1086,7 @@ try: fontManager = pickle_load(_fmcache) + fontManager.default_size = None verbose.report("Using fontManager instance from %s" % _fmcache) except: _rebuild() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3865 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3865&view=rev Author: mdboom Date: 2007年09月20日 06:57:59 -0700 (2007年9月20日) Log Message: ----------- Go all out with iterator (rather than copy) approach, as it is much faster. Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月20日 13:57:32 UTC (rev 3864) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月20日 13:57:59 UTC (rev 3865) @@ -17,11 +17,6 @@ class RendererBase: """An abstract base class to handle drawing/rendering operations """ - # This will cache paths across rendering instances - # Each subclass of RenderBase should define this a weak-keyed - # dictionary to hold native paths - # _native_paths = weakref.WeakKeyDictionary() - def __init__(self): self._texmanager = None @@ -37,43 +32,16 @@ """ pass - def _get_cached_native_path(self, path): - native_path = self._native_paths.get(path) - if native_path is None: - print "CACHE MISS", path - native_path = self.convert_to_native_path(path) - self._native_paths[path] = native_path - return native_path - def draw_path(self, gc, path, transform, rgbFace=None): """ Handles the caching of the native path associated with the given path and calls the underlying backend's _draw_path to actually do the drawing. """ - native_path = self._get_cached_native_path(path) - self._draw_native_path(gc, native_path, transform, rgbFace) + # MGDTODO: Update docstring + raise NotImplementedError - def _draw_native_path(self, gc, native_path, transform, rgbFace): - """ - Draw the native path object with the given GraphicsContext and - transform. The transform passed in will always be affine. - """ - raise NotImplementedError - - def convert_to_native_path(self, path): - """ - Backends will normally will override this, but if they don't need any - special optimizations, they can just have the generic path data - passed to them in draw_path. - """ - return path - def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - native_marker_path = self._get_cached_native_path(marker_path) - self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) - - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): """ This method is currently underscore hidden because the draw_markers method is being used as a sentinel for newstyle @@ -94,7 +62,11 @@ vec6 = transform.as_vec6_val() ...backend dependent affine... """ + # MGDTODO: Update docstring raise NotImplementedError + + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): + raise NotImplementedError def get_image_magnification(self): """ Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月20日 13:57:32 UTC (rev 3864) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月20日 13:57:59 UTC (rev 3865) @@ -117,8 +117,8 @@ debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', 'debug-annoying') - - self.convert_to_native_path = self._renderer.convert_to_native_path + self.draw_path = self._renderer.draw_path + self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region @@ -129,16 +129,6 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') - def _draw_native_path(self, gc, path, transform, rgbFace): - return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace) - - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): - return self._renderer.draw_markers( - gc, - native_marker_path, marker_trans.get_matrix(), - path.vertices, path.codes, trans.get_matrix(), - rgbFace) - def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月20日 13:57:32 UTC (rev 3864) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月20日 13:57:59 UTC (rev 3865) @@ -365,7 +365,7 @@ Transform.__init__(self) self._inverted = None - def __array__(self): + def __array__(self, *args, **kwargs): return self.get_matrix() def _do_invalidation(self): Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月20日 13:57:32 UTC (rev 3864) +++ branches/transforms/src/_backend_agg.cpp 2007年09月20日 13:57:59 UTC (rev 3865) @@ -48,59 +48,42 @@ agg::trans_affine py_to_agg_transformation_matrix(const Py::Object& obj) { PyArrayObject* matrix = NULL; - double a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0; - try { - matrix = (PyArrayObject*) PyArray_ContiguousFromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!matrix || matrix->nd != 2 || matrix->dimensions[0] != 3 || matrix->dimensions[1] != 3) { - throw Py::ValueError("Invalid affine transformation matrix."); + matrix = (PyArrayObject*) PyArray_FromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!matrix) { + throw Py::Exception(); } - - size_t stride0 = matrix->strides[0]; - size_t stride1 = matrix->strides[1]; - char* row0 = matrix->data; - char* row1 = row0 + stride0; - - a = *(double*)(row0); - row0 += stride1; - c = *(double*)(row0); - row0 += stride1; - e = *(double*)(row0); - - b = *(double*)(row1); - row1 += stride1; - d = *(double*)(row1); - row1 += stride1; - f = *(double*)(row1); + if (matrix->nd == 2 || matrix->dimensions[0] == 3 || matrix->dimensions[1] == 3) { + size_t stride0 = matrix->strides[0]; + size_t stride1 = matrix->strides[1]; + char* row0 = matrix->data; + char* row1 = row0 + stride0; + + double a = *(double*)(row0); + row0 += stride1; + double c = *(double*)(row0); + row0 += stride1; + double e = *(double*)(row0); + + double b = *(double*)(row1); + row1 += stride1; + double d = *(double*)(row1); + row1 += stride1; + double f = *(double*)(row1); + + Py_XDECREF(matrix); + + return agg::trans_affine(a, b, c, d, e, f); + } } catch (...) { - Py_XDECREF(matrix); + } Py_XDECREF(matrix); - - return agg::trans_affine(a, b, c, d, e, f); + throw Py::TypeError("Invalid affine transformation matrix"); } -/** Helper function to get the next vertex in a Numpy array of vertices. - * Will generally be used through the GET_NEXT_VERTEX macro. - */ -inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, - double& x, double& y, - size_t next_vertex_stride, - size_t next_axis_stride, - const char* & code_i, size_t code_stride) { - if (vertex_i + next_axis_stride >= vertex_end) - throw Py::ValueError("Error parsing path. Read past end of vertices"); - x = *(double*)vertex_i; - y = *(double*)(vertex_i + next_axis_stride); - vertex_i += next_vertex_stride; - code_i += code_stride; -} - -#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride, code_i, code_stride) - Py::Object BufferRegion::to_string(const Py::Tuple &args) { - // owned=true to prevent memory leak return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); } @@ -145,7 +128,6 @@ double* pv = (double*)(vertices->data + (idx * vertices->strides[0])); *x = *pv++; *y = *pv; - // MGDTODO: Range check return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))]; } @@ -170,9 +152,43 @@ agg::path_cmd_curve4, agg::path_cmd_end_poly | agg::path_flags_close}; +template<class VertexSource> class conv_quantize +{ +public: + conv_quantize(VertexSource& source, bool quantize) : + m_source(&source), m_quantize(quantize) {} + + void set_source(VertexSource& source) { m_source = &source; } + + void rewind(unsigned path_id) + { + m_source->rewind(path_id); + } + + unsigned vertex(double* x, double* y) + { + unsigned cmd = m_source->vertex(x, y); + if(m_quantize && agg::is_vertex(cmd)) + { + *x = (int)(*x); + *y = (int)(*y); + } + return cmd; + } + + void activate(bool quantize) { + m_quantize = quantize; + } + +private: + VertexSource* m_source; + bool m_quantize; +}; + + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), - cliprect(NULL), clippath(NULL), + cliprect(NULL), Ndash(0), dashOffset(0.0), dasha(NULL) { _VERBOSE("GCAgg::GCAgg"); @@ -316,15 +332,7 @@ _VERBOSE("GCAgg::_set_clip_path"); - Py_XINCREF(clippath); - clippath = NULL; - - Py::Object o = gc.getAttr("_clippath"); - if (o.ptr()==Py_None) { - return; - } - - clippath = new PathAgg(o); + clippath = gc.getAttr("_clippath"); } @@ -337,8 +345,7 @@ height(height), dpi(dpi), NUMBYTES(width*height*4), - debug(debug), - lastclippath(NULL) + debug(debug) { _VERBOSE("RendererAgg::RendererAgg"); unsigned stride(width*4); @@ -599,7 +606,7 @@ Py::Object RendererAgg::draw_markers(const Py::Tuple& args) { - typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_transform<PathIterator> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; @@ -607,27 +614,29 @@ theRasterizer->reset_clipping(); - args.verify_length(7); + args.verify_length(5, 6); GCAgg gc = GCAgg(args[0], dpi); Py::Object marker_path_obj = args[1]; - if (!PathAgg::check(marker_path_obj)) - throw Py::TypeError("Native path object is not of correct type"); - PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr()); agg::trans_affine marker_trans = py_to_agg_transformation_matrix(args[2]); - Py::Object vertices_obj = args[3]; - Py::Object codes_obj = args[4]; - agg::trans_affine trans = py_to_agg_transformation_matrix(args[5]); - facepair_t face = _get_rgba_face(args[6], gc.alpha); + Py::Object path_obj = args[3]; + agg::trans_affine trans = py_to_agg_transformation_matrix(args[4]); + Py::Object face_obj; + if (args.size() == 6) + face_obj = args[5]; + facepair_t face = _get_rgba_face(face_obj, gc.alpha); // Deal with the difference in y-axis direction marker_trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - marker_path->rewind(0); - transformed_path_t marker_path_transformed(*marker_path, marker_trans); + PathIterator marker_path(marker_path_obj); + transformed_path_t marker_path_transformed(marker_path, marker_trans); curve_t marker_path_curve(marker_path_transformed); + + PathIterator path(path_obj); + transformed_path_t path_transformed(path, trans); //maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; @@ -635,19 +644,8 @@ agg::int8u* fillCache = NULL; agg::int8u* strokeCache = NULL; - PyArrayObject* vertices = NULL; - PyArrayObject* codes = NULL; try { - vertices = (PyArrayObject*)PyArray_ContiguousFromObject - (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) - throw Py::ValueError("Invalid vertices array."); - codes = (PyArrayObject*)PyArray_ContiguousFromObject - (codes_obj.ptr(), PyArray_UINT8, 1, 1); - if (!codes) - throw Py::ValueError("Invalid codes array."); - unsigned fillSize = 0; if (face.first) { theRasterizer->add_path(marker_path_curve); @@ -681,53 +679,29 @@ rendererBase->clip_box(l, height-(b+h),l+w, height-b); } - size_t next_vertex_stride = vertices->strides[0]; - size_t next_axis_stride = vertices->strides[1]; - size_t code_stride = codes->strides[0]; - - const char* vertex_i = vertices->data; - const char* code_i = codes->data; - const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]); - - size_t N = codes->dimensions[0]; double x, y; agg::serialized_scanlines_adaptor_aa8 sa; agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; - for (size_t i=0; i < N; i++) { - size_t num_vertices = NUM_VERTICES[(int)(*code_i)]; - if (num_vertices) { - for (size_t j=0; j<num_vertices; ++j) - GET_NEXT_VERTEX(x, y); - if (*code_i == STOP || *code_i == CLOSEPOLY) - continue; - - trans.transform(&x, &y); - - if (face.first) { - //render the fill - sa.init(fillCache, fillSize, x, y); - rendererAA->color(face.second); - agg::render_scanlines(sa, sl, *rendererAA); - } - - //render the stroke - sa.init(strokeCache, strokeSize, x, y); - rendererAA->color(gc.color); + while (path_transformed.vertex(&x, &y) != agg::path_cmd_stop) { + if (face.first) { + //render the fill + sa.init(fillCache, fillSize, x, y); + rendererAA->color(face.second); agg::render_scanlines(sa, sl, *rendererAA); } - code_i += code_stride; + + //render the stroke + sa.init(strokeCache, strokeSize, x, y); + rendererAA->color(gc.color); + agg::render_scanlines(sa, sl, *rendererAA); } } catch(...) { - Py_XDECREF(vertices); - Py_XDECREF(codes); delete[] fillCache; delete[] strokeCache; } - Py_XDECREF(vertices); - Py_XDECREF(codes); delete [] fillCache; delete [] strokeCache; @@ -888,98 +862,12 @@ } -Py::Object -RendererAgg::convert_to_native_path(const Py::Tuple& args) { - _VERBOSE("RendererAgg::draw_image"); - args.verify_length(1); - - Py::Object path = args[0]; - return Py::asObject(new PathAgg(path)); -} - - -PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) { - Py::Object vertices_obj = path_obj.getAttr("vertices"); - Py::Object codes_obj = path_obj.getAttr("codes"); - - PyArrayObject* vertices = NULL; - PyArrayObject* codes = NULL; - - try { - vertices = (PyArrayObject*)PyArray_ContiguousFromObject - (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) - throw Py::ValueError("Invalid vertices array."); - codes = (PyArrayObject*)PyArray_ContiguousFromObject - (codes_obj.ptr(), PyArray_UINT8, 1, 1); - if (!codes) - throw Py::ValueError("Invalid codes array."); - - size_t next_vertex_stride = vertices->strides[0]; - size_t next_axis_stride = vertices->strides[1]; - size_t code_stride = codes->strides[0]; - - const char* vertex_i = vertices->data; - const char* code_i = codes->data; - const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]); - - size_t N = codes->dimensions[0]; - double x0, y0, x1, y1, x2, y2; - - for (size_t i = 0; i < N; ++i) { - switch (*(unsigned char*)(code_i)) { - case STOP: - GET_NEXT_VERTEX(x0, y0); - _VERBOSE("STOP"); - // MGDTODO: If this isn't the end, we should raise an error - break; - case MOVETO: - GET_NEXT_VERTEX(x0, y0); - move_to(x0, y0); - _VERBOSE("MOVETO"); - break; - case LINETO: - GET_NEXT_VERTEX(x0, y0); - line_to(x0, y0); - _VERBOSE("LINETO"); - break; - case CURVE3: - GET_NEXT_VERTEX(x0, y0); - GET_NEXT_VERTEX(x1, y1); - curve3(x0, y0, x1, y1); - curvy = true; - _VERBOSE("CURVE3"); - break; - case CURVE4: - GET_NEXT_VERTEX(x0, y0); - GET_NEXT_VERTEX(x1, y1); - GET_NEXT_VERTEX(x2, y2); - curve4(x0, y0, x1, y1, x2, y2); - curvy = true; - _VERBOSE("CURVE4"); - break; - case CLOSEPOLY: - close_polygon(); - GET_NEXT_VERTEX(x0, y0); - _VERBOSE("CLOSEPOLY"); - break; - } - } - } catch(...) { - Py_XDECREF(vertices); - Py_XDECREF(codes); - throw; - } - - Py_XDECREF(vertices); - Py_XDECREF(codes); -} - Py::Object RendererAgg::draw_path(const Py::Tuple& args) { typedef agg::conv_transform<PathIterator> transformed_path_t; - typedef agg::conv_curve<transformed_path_t> curve_t; + typedef conv_quantize<transformed_path_t> quantize_t; + typedef agg::conv_curve<quantize_t> curve_t; typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; typedef agg::conv_stroke<dash_t> stroke_dash_t; @@ -991,61 +879,59 @@ theRasterizer->reset_clipping(); _VERBOSE("RendererAgg::draw_path"); - args.verify_length(4); + args.verify_length(3, 4); - GCAgg gc = GCAgg(args[0], dpi); + Py::Object gc_obj = args[0]; Py::Object path_obj = args[1]; -// if (!PathAgg::check(path_obj)) -// throw Py::TypeError("Native path object is not of correct type"); - // PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); PathIterator path(path_obj); agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]); - facepair_t face = _get_rgba_face(args[3], gc.alpha); trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - transformed_path_t* tpath = NULL; - agg::path_storage new_path; + bool snap = false; + if (path.total_vertices() == 2) { + double x0, y0, x1, y1; + path.vertex(0, &x0, &y0); + trans.transform(&x0, &y0); + path.vertex(1, &x1, &y1); + trans.transform(&x1, &y1); + snap = ((int)x0 == (int)x1) || ((int)y0 == (int)y1); + } - bool has_clippath = (gc.clippath != NULL); + GCAgg gc = GCAgg(gc_obj, dpi, snap); + Py::Object face_obj; + if (args.size() == 4) + face_obj = args[3]; + facepair_t face = _get_rgba_face(face_obj, gc.alpha); - if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) { -// rendererBaseAlphaMask->clear(agg::gray8(0, 0)); -// gc.clippath->rewind(0); -// transformed_path_t transformed_clippath(*(gc.clippath), trans); -// theRasterizer->add_path(transformed_clippath); -// rendererAlphaMask->color(agg::gray8(255, 255)); -// agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); -// lastclippath = gc.clippath; -// lastclippath_transform = trans; + bool has_clippath = (gc.clippath.ptr() != Py_None); + + if (has_clippath && + (gc.clippath.ptr() != lastclippath.ptr() || trans != lastclippath_transform)) { + PathIterator clippath(gc.clippath); + rendererBaseAlphaMask->clear(agg::gray8(0, 0)); + transformed_path_t transformed_clippath(clippath, trans); + agg::conv_curve<transformed_path_t> curved_clippath(transformed_clippath); + theRasterizer->add_path(curved_clippath); + rendererAlphaMask->color(agg::gray8(255, 255)); + agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); + lastclippath = gc.clippath; + lastclippath_transform = trans; } try { // If this is a straight horizontal or vertical line, quantize to nearest // pixels -// if (path.total_vertices() == 2) { -// double x0, y0, x1, y1; -// path.vertex(0, &x0, &y0); -// trans.transform(&x0, &y0); -// path.vertex(1, &x1, &y1); -// trans.transform(&x1, &y1); -// if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { -// new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); -// new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); -// tpath = new transformed_path_t(new_path, agg::trans_affine()); -// } -// } - if (!tpath) { - tpath = new transformed_path_t(path, trans); - } + transformed_path_t tpath(path, trans); + quantize_t quantized(tpath, snap); // Benchmarking shows that there is no noticable slowdown to always // treating paths as having curved segments. Doing so greatly // simplifies the code - curve_t curve(*tpath); + curve_t curve(quantized); set_clipbox_rasterizer(gc.cliprect); @@ -1106,12 +992,11 @@ } } } catch (...) { - delete tpath; + // MGDTODO: We don't have anything on the heap, so this catch + // clause isn't really necessary, but we might again soon... throw; } - delete tpath; - return Py::Object(); } @@ -1446,11 +1331,9 @@ behaviors().doc("The agg backend extension module"); add_varargs_method("draw_path", &RendererAgg::draw_path, - "draw_path(gc, rgbFace, native_path, transform)\n"); - add_varargs_method("convert_to_native_path", &RendererAgg::convert_to_native_path, - "convert_to_native_path(vertices, codes)\n"); + "draw_path(gc, path, transform, rgbFace)\n"); add_varargs_method("draw_markers", &RendererAgg::draw_markers, - "draw_markers(gc, marker_path, marker_trans, vertices, codes, rgbFace)\n"); + "draw_markers(gc, marker_path, marker_trans, path, rgbFace)\n"); add_varargs_method("draw_text_image", &RendererAgg::draw_text_image, "draw_text_image(font_image, x, y, r, g, b, a)\n"); add_varargs_method("draw_image", &RendererAgg::draw_image, @@ -1476,12 +1359,6 @@ "restore_region(region)"); } -void PathAgg::init_type() -{ - behaviors().name("PathAgg"); - behaviors().doc("A native Agg path object"); -} - extern "C" DL_EXPORT(void) init_backend_agg(void) Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007年09月20日 13:57:32 UTC (rev 3864) +++ branches/transforms/src/_backend_agg.h 2007年09月20日 13:57:59 UTC (rev 3865) @@ -105,20 +105,6 @@ }; }; -// A completely opaque data type used only to pass native path -// data to/from Python. Python can't do anything with the data -// other than create and then use it. -class PathAgg : - public agg::path_storage, - public Py::PythonExtension<PathAgg> { -public: - static void init_type(void); - - PathAgg(const Py::Object& path_obj); - - bool curvy; -}; - class GCAgg { public: GCAgg(const Py::Object& gc, double dpi, bool snapto=false); @@ -126,7 +112,6 @@ ~GCAgg() { delete [] dasha; delete [] cliprect; - Py_XINCREF(clippath); } double dpi; @@ -142,7 +127,7 @@ agg::rgba color; double *cliprect; - PathAgg *clippath; + Py::Object clippath; //dashes size_t Ndash; double dashOffset; @@ -183,7 +168,6 @@ Py::Object draw_text_image(const Py::Tuple & args); Py::Object draw_image(const Py::Tuple & args); Py::Object draw_path(const Py::Tuple & args); - Py::Object convert_to_native_path(const Py::Tuple & args); Py::Object write_rgba(const Py::Tuple & args); Py::Object write_png(const Py::Tuple & args); @@ -240,7 +224,7 @@ void set_clipbox_rasterizer( double *cliprect); private: - PathAgg *lastclippath; + Py::Object lastclippath; agg::trans_affine lastclippath_transform; }; @@ -253,7 +237,6 @@ : Py::ExtensionModule<_backend_agg_module>( "_backend_agg" ) { RendererAgg::init_type(); - PathAgg::init_type(); add_keyword_method("RendererAgg", &_backend_agg_module::new_renderer, "RendererAgg(width, height, dpi)"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3864 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3864&view=rev Author: mdboom Date: 2007年09月20日 06:57:32 -0700 (2007年9月20日) Log Message: ----------- Simplification of marker paths. Modified Paths: -------------- branches/transforms/lib/matplotlib/lines.py Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月20日 13:50:27 UTC (rev 3863) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月20日 13:57:32 UTC (rev 3864) @@ -815,7 +815,7 @@ path, self.get_transform()) - _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]], closed=False) def _draw_tickleft(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(-offset, 1.0) @@ -830,7 +830,7 @@ path, self.get_transform()) - _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]], closed=False) def _draw_tickup(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, offset) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3863 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3863&view=rev Author: jdh2358 Date: 2007年09月20日 06:50:27 -0700 (2007年9月20日) Log Message: ----------- added gradient bar example Added Paths: ----------- trunk/matplotlib/examples/gradient_bar.py Added: trunk/matplotlib/examples/gradient_bar.py =================================================================== --- trunk/matplotlib/examples/gradient_bar.py (rev 0) +++ trunk/matplotlib/examples/gradient_bar.py 2007年09月20日 13:50:27 UTC (rev 3863) @@ -0,0 +1,26 @@ +from pylab import figure, show, nx, cm + +def gbar(ax, x, y, width=0.5, bottom=0): + X = [[.6, .6],[.7,.7]] + for left,top in zip(x, y): + right = left+width + ax.imshow(X, interpolation='bicubic', cmap=cm.Blues, + extent=(left, right, bottom, top), alpha=1) + +fig = figure() + +xmin, xmax = xlim = 0,10 +ymin, ymax = ylim = 0,1 +ax = fig.add_subplot(111, xlim=xlim, ylim=ylim, + autoscale_on=False) +X = [[.6, .6],[.7,.7]] + +ax.imshow(X, interpolation='bicubic', cmap=cm.copper, + extent=(xmin, xmax, ymin, ymax), alpha=1) + +N = 10 +x = nx.arange(N)+0.25 +y = nx.mlab.rand(N) +gbar(ax, x, y, width=0.7) +ax.set_aspect('normal') +show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3862 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3862&view=rev Author: mdboom Date: 2007年09月20日 05:40:41 -0700 (2007年9月20日) Log Message: ----------- Removing debugging output in last commit. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2007年09月20日 12:31:26 UTC (rev 3861) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2007年09月20日 12:40:41 UTC (rev 3862) @@ -773,7 +773,6 @@ else: if is_string_like(size): parent_size = fontManager.get_default_size() - print "parent_size", parent_size, size scaling = font_scalings.get(size) if scaling is not None: size = parent_size * scaling This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3861 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3861&view=rev Author: mdboom Date: 2007年09月20日 05:31:26 -0700 (2007年9月20日) Log Message: ----------- Fix font.size from being saved in the fontManager.cache Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2007年09月19日 19:48:17 UTC (rev 3860) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2007年09月20日 12:31:26 UTC (rev 3861) @@ -773,6 +773,7 @@ else: if is_string_like(size): parent_size = fontManager.get_default_size() + print "parent_size", parent_size, size scaling = font_scalings.get(size) if scaling is not None: size = parent_size * scaling @@ -843,10 +844,9 @@ """ def __init__(self, size=None, weight='normal'): - if not size : size = rcParams['font.size'] - self.__default_size = size self.__default_weight = weight - + self.default_size = size + paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), os.path.join(rcParams['datapath'],'fonts','afm')] @@ -899,7 +899,9 @@ def get_default_size(self): "Return the default font size." - return self.__default_size + if self.default_size is None: + return rcParams['font.size'] + return self.default_size def set_default_weight(self, weight): "Set the default font weight. The initial value is 'normal'." @@ -1085,6 +1087,7 @@ try: fontManager = pickle_load(_fmcache) + fontManager.default_size = None verbose.report("Using fontManager instance from %s" % _fmcache) except: _rebuild() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3860 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3860&view=rev Author: mdboom Date: 2007年09月19日 12:48:17 -0700 (2007年9月19日) Log Message: ----------- Use iterator rather than caching approach for paths Modified Paths: -------------- branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月19日 19:46:34 UTC (rev 3859) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月19日 19:48:17 UTC (rev 3860) @@ -112,8 +112,7 @@ self.dpi = dpi self.width = width self.height = height - if __debug__: verbose.report('RendererAgg.__init__ width=%s, \ - height=%s'%(width, height), 'debug-annoying') + if __debug__: verbose.report('RendererAgg.__init__ width=%s, height=%s'%(width, height), 'debug-annoying') self._renderer = _RendererAgg(int(width), int(height), dpi, debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月19日 19:46:34 UTC (rev 3859) +++ branches/transforms/src/_backend_agg.cpp 2007年09月19日 19:48:17 UTC (rev 3860) @@ -87,15 +87,17 @@ inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, double& x, double& y, size_t next_vertex_stride, - size_t next_axis_stride) { + size_t next_axis_stride, + const char* & code_i, size_t code_stride) { if (vertex_i + next_axis_stride >= vertex_end) throw Py::ValueError("Error parsing path. Read past end of vertices"); x = *(double*)vertex_i; y = *(double*)(vertex_i + next_axis_stride); vertex_i += next_vertex_stride; + code_i += code_stride; } -#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) +#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride, code_i, code_stride) Py::Object BufferRegion::to_string(const Py::Tuple &args) { @@ -103,7 +105,71 @@ return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); } +class PathIterator { + PyArrayObject* vertices; + PyArrayObject* codes; + size_t m_iterator; + size_t m_total_vertices; +public: + PathIterator(const Py::Object& path_obj) : + vertices(NULL), codes(NULL), m_iterator(0) { + Py::Object vertices_obj = path_obj.getAttr("vertices"); + Py::Object codes_obj = path_obj.getAttr("codes"); + + vertices = (PyArrayObject*)PyArray_ContiguousFromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) + throw Py::ValueError("Invalid vertices array."); + codes = (PyArrayObject*)PyArray_ContiguousFromObject + (codes_obj.ptr(), PyArray_UINT8, 1, 1); + if (!codes) + throw Py::ValueError("Invalid codes array."); + + if (codes->dimensions[0] != vertices->dimensions[0]) + throw Py::ValueError("Vertices and codes array are not the same length."); + + m_total_vertices = codes->dimensions[0]; + } + + ~PathIterator() { + Py_XDECREF(vertices); + Py_XDECREF(codes); + } + + static const char code_map[]; + + inline unsigned vertex(unsigned idx, double* x, double* y) { + if (idx > m_total_vertices) + throw Py::RuntimeError("Requested vertex past end"); + double* pv = (double*)(vertices->data + (idx * vertices->strides[0])); + *x = *pv++; + *y = *pv; + // MGDTODO: Range check + return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))]; + } + + inline unsigned vertex(double* x, double* y) { + if(m_iterator >= m_total_vertices) return agg::path_cmd_stop; + return vertex(m_iterator++, x, y); + } + + inline void rewind(unsigned path_id) { + m_iterator = path_id; + } + + inline unsigned total_vertices() { + return m_total_vertices; + } +}; + +const char PathIterator::code_map[] = {0, + agg::path_cmd_move_to, + agg::path_cmd_line_to, + agg::path_cmd_curve3, + agg::path_cmd_curve4, + agg::path_cmd_end_poly | agg::path_flags_close}; + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), @@ -634,7 +700,7 @@ if (num_vertices) { for (size_t j=0; j<num_vertices; ++j) GET_NEXT_VERTEX(x, y); - if (code_i == IGNORE) + if (*code_i == STOP || *code_i == CLOSEPOLY) continue; trans.transform(&x, &y); @@ -863,9 +929,10 @@ for (size_t i = 0; i < N; ++i) { switch (*(unsigned char*)(code_i)) { - case IGNORE: + case STOP: GET_NEXT_VERTEX(x0, y0); - _VERBOSE("IGNORE"); + _VERBOSE("STOP"); + // MGDTODO: If this isn't the end, we should raise an error break; case MOVETO: GET_NEXT_VERTEX(x0, y0); @@ -894,10 +961,10 @@ break; case CLOSEPOLY: close_polygon(); + GET_NEXT_VERTEX(x0, y0); _VERBOSE("CLOSEPOLY"); break; } - code_i += code_stride; } } catch(...) { Py_XDECREF(vertices); @@ -911,7 +978,7 @@ Py::Object RendererAgg::draw_path(const Py::Tuple& args) { - typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_transform<PathIterator> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; @@ -928,9 +995,11 @@ GCAgg gc = GCAgg(args[0], dpi); Py::Object path_obj = args[1]; - if (!PathAgg::check(path_obj)) - throw Py::TypeError("Native path object is not of correct type"); - PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); +// if (!PathAgg::check(path_obj)) +// throw Py::TypeError("Native path object is not of correct type"); + // PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); + PathIterator path(path_obj); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]); facepair_t face = _get_rgba_face(args[3], gc.alpha); @@ -943,34 +1012,34 @@ bool has_clippath = (gc.clippath != NULL); if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) { - rendererBaseAlphaMask->clear(agg::gray8(0, 0)); - gc.clippath->rewind(0); - transformed_path_t transformed_clippath(*(gc.clippath), trans); - theRasterizer->add_path(transformed_clippath); - rendererAlphaMask->color(agg::gray8(255, 255)); - agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); - lastclippath = gc.clippath; - lastclippath_transform = trans; +// rendererBaseAlphaMask->clear(agg::gray8(0, 0)); +// gc.clippath->rewind(0); +// transformed_path_t transformed_clippath(*(gc.clippath), trans); +// theRasterizer->add_path(transformed_clippath); +// rendererAlphaMask->color(agg::gray8(255, 255)); +// agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); +// lastclippath = gc.clippath; +// lastclippath_transform = trans; } try { // If this is a straight horizontal or vertical line, quantize to nearest // pixels - if (path->total_vertices() == 2) { - double x0, y0, x1, y1; - path->vertex(0, &x0, &y0); - trans.transform(&x0, &y0); - path->vertex(1, &x1, &y1); - trans.transform(&x1, &y1); - if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { - new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); - new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); - tpath = new transformed_path_t(new_path, agg::trans_affine()); - } - } +// if (path.total_vertices() == 2) { +// double x0, y0, x1, y1; +// path.vertex(0, &x0, &y0); +// trans.transform(&x0, &y0); +// path.vertex(1, &x1, &y1); +// trans.transform(&x1, &y1); +// if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { +// new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); +// new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); +// tpath = new transformed_path_t(new_path, agg::trans_affine()); +// } +// } if (!tpath) { - tpath = new transformed_path_t(*path, trans); + tpath = new transformed_path_t(path, trans); } // Benchmarking shows that there is no noticable slowdown to always Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007年09月19日 19:46:34 UTC (rev 3859) +++ branches/transforms/src/_backend_agg.h 2007年09月19日 19:48:17 UTC (rev 3860) @@ -40,14 +40,14 @@ #include "agg_vcgen_markers_term.h" // These are copied directly from path.py, and must be kept in sync -#define IGNORE 0 +#define STOP 0 #define MOVETO 1 #define LINETO 2 #define CURVE3 3 #define CURVE4 4 #define CLOSEPOLY 5 -const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 }; +const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 }; typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3859 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3859&view=rev Author: mdboom Date: 2007年09月19日 12:46:34 -0700 (2007年9月19日) Log Message: ----------- Lots of minor fixes Modified Paths: -------------- branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/pbox.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -252,12 +252,9 @@ if not is_numlike(self.pickradius): raise ValueError,"pick radius should be a distance" - if self._newstyle: - # transform in backend - x = self._x - y = self._y - else: - x, y = self._get_plottable() + # transform in backend + x = self._x + y = self._y if len(x)==0: return False,{} xt, yt = self.get_transform().numerix_x_y(x, y) @@ -337,7 +334,6 @@ ACCEPTS: (npy.array xdata, npy.array ydata) """ - if len(args)==1: x, y = args[0] else: @@ -347,8 +343,9 @@ self._yorig = y self.recache() + # MGDTODO: Masked data arrays are broken _masked_array_to_path_code_mapping = npy.array( - [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type) + [Path.LINETO, Path.MOVETO, Path.MOVETO], Path.code_type) def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units @@ -387,18 +384,18 @@ # MGDTODO: If _draw_steps is removed, remove the following line also self._step_path = None - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 return npy.alltrue(x[1:]-x[0:-1]>=0) + # MGDTODO: Remove me (seems to be used for old-style interface only) def _get_plottable(self): # If log scale is set, only pos data will be returned x, y = self._x, self._y - # MGDTODO: Deal with the log scale here + # MGDTODO: (log-scaling) # try: logx = self.get_transform().get_funcx().get_type()==LOG10 # except RuntimeError: logx = False # non-separable Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/patches.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -319,8 +319,6 @@ return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) - # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will - # happen automatically (without needing to call set_x etc. def __init__(self, xy, width, height, **kwargs): """ xy is an x,y tuple lower, left @@ -459,17 +457,14 @@ def __init__(self, xy, **kwargs): """ - xy is a sequence of (x,y) 2 tuples + xy is a numpy array with shape Nx2 Valid kwargs are: %(Patch)s See Patch documentation for additional kwargs """ - # MGDTODO: This should encourage the use of numpy arrays of shape Nx2 Patch.__init__(self, **kwargs) - if not isinstance(xy, list): - xy = list(xy) - self._path = Path(xy, closed=False) + self._path = Path(xy, closed=True) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_verts(self): Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/path.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -1,15 +1,13 @@ import numpy as npy -DEBUG = True - class Path(object): # Path codes - IGNORE = 0 # 1 vertex + STOP = 0 # 1 vertex MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices - CLOSEPOLY = 5 + CLOSEPOLY = 5 # 1 vertex ### # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, # so if they don't, we probably shouldn't @@ -18,38 +16,36 @@ UBSPLINE = 8 #### - NUM_VERTICES = [1, 1, 1, 2, 3, 0] + NUM_VERTICES = [1, 1, 1, 2, 3, 1] code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): - self._vertices = npy.asarray(vertices, npy.float_) - assert self._vertices.ndim == 2 - assert self._vertices.shape[1] == 2 - + vertices = npy.asarray(vertices, npy.float_) + assert vertices.ndim == 2 + assert vertices.shape[1] == 2 + if codes is None: if closed: codes = self.LINETO * npy.ones( - self._vertices.shape[0] + 1, self.code_type) + vertices.shape[0] + 1, self.code_type) codes[0] = self.MOVETO - codes[-1] = self.CLOSEPOLY + codes[-1] = self.CLOSEPOLY + vertices = npy.concatenate((vertices, [[0.0, 0.0]])) else: codes = self.LINETO * npy.ones( - self._vertices.shape[0], self.code_type) + vertices.shape[0], self.code_type) codes[0] = self.MOVETO else: codes = npy.asarray(codes, self.code_type) - self._codes = codes - + assert codes.ndim == 1 + assert len(codes) == len(vertices) + + self._codes = codes + self._vertices = vertices + assert self._codes.ndim == 1 - if DEBUG: - i = 0 - NUM_VERTICES = self.NUM_VERTICES - for code in codes: - i += NUM_VERTICES[code] - assert i == len(self.vertices) - def __repr__(self): return "Path(%s, %s)" % (self.vertices, self.codes) @@ -66,11 +62,13 @@ NUM_VERTICES = self.NUM_VERTICES vertices = self.vertices for code in self.codes: - num_vertices = NUM_VERTICES[code] - if num_vertices >= 1: - i += num_vertices - 1 - yield vertices[i] - i += 1 + if code == self.CLOSEPOLY: + i += 1 + else: + num_vertices = NUM_VERTICES[code] + i += num_vertices - 1 + yield vertices[i] + i += 1 _unit_rectangle = None #@classmethod @@ -118,16 +116,18 @@ [-offset, -1.0], [-1.0, -offset], - [-1.0, 0.0]], - npy.float_) - codes = npy.array( - [cls.MOVETO, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CLOSEPOLY], - cls.code_type) + [-1.0, 0.0], + + [-1.0, 0.0]], + npy.float_) + + codes = cls.CURVE4 + npy.ones((len(vertices))) + codes[0] = cls.MOVETO + codes[-1] = cls.CLOSEPOLY + cls._unit_circle = Path(vertices, codes) return cls._unit_circle unit_circle = classmethod(unit_circle) + +# MGDTODO: Add a transformed path that would automatically invalidate +# itself when its transform changes Modified: branches/transforms/lib/matplotlib/pbox.py =================================================================== --- branches/transforms/lib/matplotlib/pbox.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/pbox.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -1,5 +1,3 @@ -# MGDTODO: Just included verbatim for now - class PBox(list): ''' A left-bottom-width-height (lbwh) specification of a bounding box, Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/text.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -231,7 +231,7 @@ # now rotate the bbox - cornersRotated = M(cornersHoriz) + cornersRotated = M.transform(cornersHoriz) txs = cornersRotated[:, 0] tys = cornersRotated[:, 1] @@ -269,7 +269,7 @@ # now rotate the positions around the first x,y position - xys = M(offsetLayout) + xys = M.transform(offsetLayout) tx = xys[:, 0] ty = xys[:, 1] tx += offsetx @@ -277,7 +277,7 @@ # now inverse transform back to data coords inverse_transform = self.get_transform().inverted() - xys = inverse_transform(xys) + xys = inverse_transform.transform(xys) xs, ys = zip(*xys) @@ -407,7 +407,7 @@ return (x, y, self._text, self._color, self._verticalalignment, self._horizontalalignment, hash(self._fontproperties), self._rotation, - self.get_transform().to_values(), + self.get_transform(), ) def get_text(self): Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月19日 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月19日 19:46:34 UTC (rev 3859) @@ -32,7 +32,8 @@ for child in children: getattr(self, child)._parents.add(self) self._children = children - + + class BboxBase(TransformNode): ''' This is the read-only part of a bounding-box @@ -293,6 +294,7 @@ return Bbox.from_lbrt(xmin, ymin, xmax, ymax) union = staticmethod(union) + class TransformedBbox(BboxBase): def __init__(self, bbox, transform): assert isinstance(bbox, Bbox) @@ -313,16 +315,20 @@ def get_points(self): if self._points is None: - self._points = self.transform(self.bbox.get_points()) + self._points = self.transform.transform(self.bbox.get_points()) return self._points + class Transform(TransformNode): def __init__(self): TransformNode.__init__(self) - def __call__(self, points): + def transform(self, points): raise NotImplementedError() + def transform_without_affine(self, points): + return self.transform(points), IDENTITY + def __add__(self, other): if isinstance(other, Transform): return composite_transform_factory(self, other) @@ -336,7 +342,7 @@ "Can not add Transform to object of type '%s'" % type(other)) def transform_point(self, point): - return self.__call__(npy.asarray([point]))[0] + return self.transform(npy.asarray([point]))[0] def has_inverse(self): raise NotImplementedError() @@ -350,6 +356,7 @@ def is_affine(self): return False + class Affine2DBase(Transform): input_dims = 2 output_dims = 2 @@ -390,7 +397,7 @@ def get_matrix(self): raise NotImplementedError() - def __call__(self, points): + def transform(self, points): """ Applies the transformation to an array of 2D points and returns the result. @@ -414,6 +421,11 @@ points = npy.dot(mtx[0:2, 0:2], points) points = points + mtx[0:2, 2:] return points.transpose() + + def transform_without_affine(self, points): + # MGDTODO: Should we copy the points here? I'd like to avoid it, + # if possible + return points, self def inverted(self): if self._inverted is None: @@ -430,9 +442,6 @@ class Affine2D(Affine2DBase): - input_dims = 2 - output_dims = 2 - def __init__(self, matrix = None): """ Initialize an Affine transform from a 3x3 numpy float array. @@ -535,40 +544,82 @@ def is_affine(self): return True + +IDENTITY = Affine2D() class BlendedGenericTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): # Here we ask: "Does it blend?" assert x_transform.is_separable() assert y_transform.is_separable() - + assert x_transform.input_dims == x_transform.output_dims == 2 + assert y_transform.input_dims == y_transform.output_dims == 2 + Transform.__init__(self) self._x = x_transform self._y = y_transform self.set_children(['_x', '_y']) - def __call__(self, points): - if self._x == self._y: + def transform(self, points): + # MGDTODO: Optimize the case where one of these is + # an affine + x = self._x + y = self._y + if x == y and x.input_dims == 2: return self._x(points) - - x_points = self._x(points) - y_points = self._y(points) - # This works because we already know the transforms are - # separable - return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) -# def set_x_transform(self, x_transform): -# self.replace_child(0, x_transform) + if x.input_dims == 2: + x_points = x.transform(points)[:, 0] + else: + x_points = x.transform(points[:, 0]) -# def set_y_transform(self, y_transform): -# self.replace_child(1, y_transform) + if y.input_dims == 2: + y_points = y.transform(points)[:, 1] + else: + y_points = y.transform(points[:, 1]) + return npy.vstack((x_points, y_points)).transpose() + + def inverted(self): + return BlendedGenericTransform(self._x.inverted(), self._y.inverted()) -class BlendedAffine2D(Affine2DBase, BlendedGenericTransform): + def is_separable(self): + return True + + +class BlendedSeparableTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): + # Here we ask: "Does it blend?" + assert x_transform.is_separable() + assert y_transform.is_separable() + assert x_transform.input_dims == x.transform.output_dims == 1 + assert y_transform.input_dims == y.transform.output_dims == 1 + + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) + + def transform(self, points): + x_points = self._x(points[:, 0]) + y_points = self._y(points[:, 1]) + return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose() + + +class BlendedAffine2D(Affine2DBase, Transform): + def __init__(self, x_transform, y_transform): assert x_transform.is_affine() assert y_transform.is_affine() - BlendedGenericTransform.__init__(self, x_transform, y_transform) + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) Affine2DBase.__init__(self) self._mtx = None @@ -597,12 +648,14 @@ # c to zero. self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) return self._mtx - + + def blended_transform_factory(x_transform, y_transform): if x_transform.is_affine() and y_transform.is_affine(): return BlendedAffine2D(x_transform, y_transform) return BlendedGenericTransform(x_transform, y_transform) + class CompositeGenericTransform(Transform): def __init__(self, a, b): assert a.output_dims == b.input_dims @@ -614,9 +667,17 @@ self._b = b self.set_children(['_a', '_b']) - def __call__(self, points): - return self._b(self._a(points)) + def transform(self, points): + return self._b.transform(self._a.transform(points)) + + def inverted(self): + return CompositeGenericTransform(self._b.inverted(), self._a.inverted()) + def is_separable(self): + return True + return self._a.is_separable() and self._b.is_separable() + + class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): assert a.is_affine() @@ -643,11 +704,32 @@ self._b.get_matrix()) return self._mtx + def composite_transform_factory(a, b): if a.is_affine() and b.is_affine(): return CompositeAffine2D(a, b) return CompositeGenericTransform(a, b) + + +class LogTransform(Transform): + input_dims = 1 + output_dims = 1 + def transform(self, a): + m = npy.ma.masked_where(a < 0, a) + return npy.log10(m) + + +class TestLogTransform(Transform): + input_dims = 2 + output_dims = 2 + def transform(self, xy): + return xy * 2 + + def inverted(self): + return self + + class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): assert isinstance(boxin, BboxBase) @@ -688,6 +770,7 @@ self._mtx = affine._mtx return self._mtx + def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Ensure the endpoints of a range are not too close together. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3858 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3858&view=rev Author: mdboom Date: 2007年09月19日 09:18:51 -0700 (2007年9月19日) Log Message: ----------- Got steps_demo.py working Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月19日 16:18:51 UTC (rev 3858) @@ -40,7 +40,7 @@ def _get_cached_native_path(self, path): native_path = self._native_paths.get(path) if native_path is None: - # print "CACHE MISS", path + print "CACHE MISS", path native_path = self.convert_to_native_path(path) self._native_paths[path] = native_path return native_path Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月19日 16:18:51 UTC (rev 3858) @@ -25,53 +25,6 @@ (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN) = range(8) -def unmasked_index_ranges(mask, compressed = True): - ''' - Calculate the good data ranges in a masked 1-D npy.array, based on mask. - - Returns Nx2 npy.array with each row the start and stop indices - for slices of the compressed npy.array corresponding to each of N - uninterrupted runs of unmasked values. - If optional argument compressed is False, it returns the - start and stop indices into the original npy.array, not the - compressed npy.array. - Returns None if there are no unmasked values. - - Example: - - y = ma.array(npy.arange(5), mask = [0,0,1,0,0]) - #ii = unmasked_index_ranges(y.mask()) - ii = unmasked_index_ranges(ma.getmask(y)) - # returns [[0,2,] [2,4,]] - - y.compressed().filled()[ii[1,0]:ii[1,1]] - # returns npy.array [3,4,] - # (The 'filled()' method converts the masked npy.array to a numerix npy.array.) - - #i0, i1 = unmasked_index_ranges(y.mask(), compressed=False) - i0, i1 = unmasked_index_ranges(ma.getmask(y), compressed=False) - # returns [[0,3,] [2,5,]] - - y.filled()[ii[1,0]:ii[1,1]] - # returns npy.array [3,4,] - - ''' - m = npy.concatenate(((1,), mask, (1,))) - indices = npy.arange(len(mask) + 1) - mdif = m[1:] - m[:-1] - i0 = npy.compress(mdif == -1, indices) - i1 = npy.compress(mdif == 1, indices) - assert len(i0) == len(i1) - if len(i1) == 0: - return None - if not compressed: - return npy.concatenate((i0[:, npy.newaxis], i1[:, npy.newaxis]), axis=1) - seglengths = i1 - i0 - breakpoints = npy.cumsum(seglengths) - ic0 = npy.concatenate(((0,), breakpoints[:-1])) - ic1 = breakpoints - return npy.concatenate((ic0[:, npy.newaxis], ic1[:, npy.newaxis]), axis=1) - def segment_hits(cx,cy,x,y,radius): """Determine if any line segments are within radius of a point. Returns the list of line segments that are within that radius. @@ -117,7 +70,7 @@ '--' : '_draw_dashed', '-.' : '_draw_dash_dot', ':' : '_draw_dotted', - 'steps': '_draw_solid', + 'steps': '_draw_steps', 'None' : '_draw_nothing', ' ' : '_draw_nothing', '' : '_draw_nothing', @@ -394,6 +347,8 @@ self._yorig = y self.recache() + _masked_array_to_path_code_mapping = npy.array( + [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type) def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units @@ -411,36 +366,28 @@ if len(x) != len(y): raise RuntimeError('xdata and ydata must be the same length') - # MGDTODO: Deal with segments + self._xy = npy.vstack((npy.asarray(x, npy.float_), + npy.asarray(y, npy.float_))).transpose() + self._x = self._xy[:, 0] # just a view + self._y = self._xy[:, 1] # just a view + self._logcache = None + mx = ma.getmask(x) my = ma.getmask(y) mask = ma.mask_or(mx, my) + codes = None if mask is not ma.nomask: - x = ma.masked_array(x, mask=mask).compressed() - y = ma.masked_array(y, mask=mask).compressed() - self._segments = unmasked_index_ranges(mask) - else: - self._segments = None - - self._xy = npy.vstack((npy.asarray(x, npy.float_), - npy.asarray(y, npy.float_))).transpose() - self._x = self._xy[:, 0] - self._y = self._xy[:, 1] - self._logcache = None + m = npy.concatenate(((1,), mask, (1,))) + mdif = m[1:] - m[:-1] + mdif = npy.maximum((mdif[:-1] * -2), mask) + codes = npy.take( + self._masked_array_to_path_code_mapping, + mdif) + self._path = Path(self._xy, codes, closed=False) + # MGDTODO: If _draw_steps is removed, remove the following line also + self._step_path = None - if self._linestyle == 'steps': - siz=len(xt) - if siz<2: return - xt, yt = self._x, self._y - xt2=npy.ones((2*siz,), xt.dtype) - xt2[0:-1:2], xt2[1:-1:2], xt2[-1] = xt, xt[1:], xt[-1] - yt2=npy.ones((2*siz,), yt.dtype) - yt2[0:-1:2], yt2[1::2] = yt, yt - self._path = Path(npy.vstack((xt2, yt2)).transpose(), closed=False) - else: - self._path = Path(self._xy, closed=False) - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 @@ -507,14 +454,7 @@ funcname = self._lineStyles.get(self._linestyle, '_draw_nothing') lineFunc = getattr(self, funcname) - - # MGDTODO: Deal with self._segments - if self._segments is not None: - for ii in self._segments: - lineFunc(renderer, gc, xt[ii[0]:ii[1]], yt[ii[0]:ii[1]]) - - else: - lineFunc(renderer, gc, self._path) + lineFunc(renderer, gc, self._path) # MGDTODO: Deal with markers if self._marker is not None: @@ -709,7 +649,29 @@ def _draw_nothing(self, renderer, gc, path): pass - + + def _draw_steps(self, renderer, gc, path): + # We generate the step function path on-the-fly, and then cache it. + # The cache may be later invalidated when the data changes + # (in self.recache()) + + # MGDTODO: Untested -- using pylab.step doesn't actually trigger + # this code -- the path is "stepped" before even getting to this + # class. Perhaps this should be removed here, since it is not as + # powerful as what is in axes.step() anyway. + if self._step_path is None: + vertices = self._path.vertices + codes = self._path.codes + siz = len(vertices) + if siz<2: return + new_vertices = npy.zeros((2*siz, 2), vertices.dtype) + new_vertices[0:-1:2, 0], new_vertices[1:-1:2, 0], newvertices[-1, 0] = vertices[:, 0], vertices[1:, 0], vertices[-1, 0] + new_vertices[0:-1:2, 1], new_vertices[1::2, 1] = vertices[:, 1], vertices[:, 1] + self._step_path = Path(new_vertices, closed=False) + gc.set_linestyle('solid') + renderer.draw_path(gc, self._step_path, self.get_transform()) + + def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/path.py 2007年09月19日 16:18:51 UTC (rev 3858) @@ -1,10 +1,10 @@ import numpy as npy -VALIDATE_PATHS = True +DEBUG = True class Path(object): # Path codes - STOP = 0 + IGNORE = 0 # 1 vertex MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices @@ -18,7 +18,7 @@ UBSPLINE = 8 #### - NUM_VERTICES = [0, 1, 1, 2, 3, 0] + NUM_VERTICES = [1, 1, 1, 2, 3, 0] code_type = npy.uint8 @@ -43,7 +43,7 @@ assert self._codes.ndim == 1 - if VALIDATE_PATHS: + if DEBUG: i = 0 NUM_VERTICES = self.NUM_VERTICES for code in codes: Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月19日 16:18:51 UTC (rev 3858) @@ -32,18 +32,6 @@ for child in children: getattr(self, child)._parents.add(self) self._children = children - - # MGDTODO: decide whether we need this in-place updating and - # remove if not -# def replace_child(self, index, child): -# children = self._children -# getattr(self, children[index])._parents.remove(self) -# setattr(self, children[index], child) -# # We have to reset children in case two or more -# # of the children are the same -# for child in children: -# getattr(self, child)._parents.add(self) -# self.invalidate() class BboxBase(TransformNode): ''' @@ -327,53 +315,7 @@ if self._points is None: self._points = self.transform(self.bbox.get_points()) return self._points - -# MGDTODO: This code probably works, but I don't think it's a good idea -# (from a code clarity perspective) -# class BlendedBbox(BboxBase): -# def __init__(self, bbox_x, bbox_y): -# assert isinstance(bbox_x, BboxBase) -# assert isinstance(bbox_y, BboxBase) - -# BboxBase.__init__(self) -# self._x = bbox_x -# self._y = bbox_y -# self.set_children(['_x', '_y']) -# self._points = None - -# def __repr__(self): -# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform) -# __str__ = __repr__ -# def _do_invalidation(self): -# self._points = None - -# def get_points(self): -# if self._points is None: -# # MGDTODO: Optimize -# if self._x == self._y: -# self._points = self._x.get_points() -# else: -# x_points = self._x.get_points() -# y_points = self._y.get_points() -# self._points = npy.array( -# [[x_points[0,0], y_points[0,1]], -# [x_points[1,0], y_points[1,1]]], -# npy.float_) -# return self._points - -# def _set_intervalx(self, pair): -# # MGDTODO: Optimize -# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]]) -# self.replace_child(0, bbox) -# intervalx = property(BboxBase._get_intervalx, _set_intervalx) - -# def _set_intervaly(self, pair): -# # MGDTODO: Optimize -# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]]) -# self.replace_child(1, bbox) -# intervaly = property(BboxBase._get_intervaly, _set_intervaly) - class Transform(TransformNode): def __init__(self): TransformNode.__init__(self) @@ -746,7 +688,6 @@ self._mtx = affine._mtx return self._mtx -# MGDTODO: There's probably a better place for this def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Ensure the endpoints of a range are not too close together. Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/src/_backend_agg.cpp 2007年09月19日 16:18:51 UTC (rev 3858) @@ -634,6 +634,9 @@ if (num_vertices) { for (size_t j=0; j<num_vertices; ++j) GET_NEXT_VERTEX(x, y); + if (code_i == IGNORE) + continue; + trans.transform(&x, &y); if (face.first) { @@ -860,6 +863,10 @@ for (size_t i = 0; i < N; ++i) { switch (*(unsigned char*)(code_i)) { + case IGNORE: + GET_NEXT_VERTEX(x0, y0); + _VERBOSE("IGNORE"); + break; case MOVETO: GET_NEXT_VERTEX(x0, y0); move_to(x0, y0); Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007年09月19日 13:28:11 UTC (rev 3857) +++ branches/transforms/src/_backend_agg.h 2007年09月19日 16:18:51 UTC (rev 3858) @@ -40,14 +40,14 @@ #include "agg_vcgen_markers_term.h" // These are copied directly from path.py, and must be kept in sync -#define STOP 0 +#define IGNORE 0 #define MOVETO 1 #define LINETO 2 #define CURVE3 3 #define CURVE4 4 #define CLOSEPOLY 5 -const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 }; +const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 }; typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3857 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3857&view=rev Author: mdboom Date: 2007年09月19日 06:28:11 -0700 (2007年9月19日) Log Message: ----------- Got legend working with new transforms Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007年09月18日 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/axes.py 2007年09月19日 13:28:11 UTC (rev 3857) @@ -632,6 +632,7 @@ self.transAxes = mtransforms.BboxTransform( mtransforms.Bbox.unit(), self.bbox) + # self.set_transform(self.transAxes) self.transData = mtransforms.BboxTransform( self.viewLim, self.bbox) @@ -724,6 +725,7 @@ self.axesPatch.set_figure(self.figure) self.axesPatch.set_transform(self.transAxes) self.axesPatch.set_linewidth(rcParams['axes.linewidth']) + # MGDTODO: What is axesFrame for? We already have axesPatch self.axesFrame = mlines.Line2D((0,1,1,0,0), (0,0,1,1,0), linewidth=rcParams['axes.linewidth'], color=rcParams['axes.edgecolor'], @@ -5201,7 +5203,7 @@ Subplot(211) # 2 rows, 1 column, first (upper) plot """ def __str__(self): - return "Subplot(%g,%g)"%(self.bottom.get(),self.left.get()) + return "Subplot(%f,%f,%f,%f)" % (self.bbox.bounds) def __init__(self, fig, *args, **kwargs): """ Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007年09月18日 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/legend.py 2007年09月19日 13:28:11 UTC (rev 3857) @@ -164,7 +164,7 @@ else: raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent - self.set_transform( BboxTransform( Bbox.unit(), parent.bbox) ) + self.set_transform( BboxTransform(Bbox.unit(), parent.bbox) ) if loc is None: loc = rcParams["legend.loc"] @@ -223,7 +223,7 @@ a.set_transform(self.get_transform()) def _approx_text_height(self): - return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height() + return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height def draw(self, renderer): @@ -531,7 +531,7 @@ def get_tbounds(text): #get text bounds in axes coords bbox = text.get_window_extent(renderer) bboxa = bbox.inverse_transformed(self.get_transform()) - return bboxa.get_bounds() + return bboxa.bounds hpos = [] for t, tabove in zip(self.texts[1:], self.texts[:-1]): @@ -560,10 +560,10 @@ # Set the data for the legend patch bbox = copy.copy(self._get_handle_text_bbox(renderer)) - bbox = bbox.scaled(1 + self.pad, 1 + self.pad) - l,b,w,h = bbox.get_bounds() - self.legendPatch.set_bounds(l,b,w,h) - + bbox = bbox.expanded(1 + self.pad, 1 + self.pad) + l, b, w, h = bbox.bounds + self.legendPatch.set_bounds(l, b, w, h) + ox, oy = 0, 0 # center if iterable(self._loc) and len(self._loc)==2: Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月18日 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月19日 13:28:11 UTC (rev 3857) @@ -117,7 +117,7 @@ '--' : '_draw_dashed', '-.' : '_draw_dash_dot', ':' : '_draw_dotted', - 'steps': '_draw_steps', + 'steps': '_draw_solid', 'None' : '_draw_nothing', ' ' : '_draw_nothing', '' : '_draw_nothing', @@ -352,10 +352,10 @@ self._picker = p def get_window_extent(self, renderer): - xys = self.get_transform()(self._xys) + xy = self.get_transform()(self._xy) - x = xys[:, 0] - y = xys[:, 1] + x = xy[:, 0] + y = xy[:, 1] left = x.min() bottom = y.min() width = x.max() - left @@ -426,9 +426,19 @@ npy.asarray(y, npy.float_))).transpose() self._x = self._xy[:, 0] self._y = self._xy[:, 1] - self._path = Path(self._xy, closed=False) - self._logcache = None + + if self._linestyle == 'steps': + siz=len(xt) + if siz<2: return + xt, yt = self._x, self._y + xt2=npy.ones((2*siz,), xt.dtype) + xt2[0:-1:2], xt2[1:-1:2], xt2[-1] = xt, xt[1:], xt[-1] + yt2=npy.ones((2*siz,), yt.dtype) + yt2[0:-1:2], yt2[1::2] = yt, yt + self._path = Path(npy.vstack((xt2, yt2)).transpose(), closed=False) + else: + self._path = Path(self._xy, closed=False) def _is_sorted(self, x): @@ -700,24 +710,6 @@ pass - def _draw_steps(self, renderer, gc, xt, yt): - # MGDTODO: This is a quirky one. The step-plotting part - # should probably be moved to where the path is generated - # in recache, and then just drawn with _draw_solid - siz=len(xt) - if siz<2: return - xt2=npy.ones((2*siz,), xt.dtype) - xt2[0:-1:2], xt2[1:-1:2], xt2[-1]=xt, xt[1:], xt[-1] - yt2=npy.ones((2*siz,), yt.dtype) - yt2[0:-1:2], yt2[1::2]=yt, yt - gc.set_linestyle('solid') - - if self._newstyle: - renderer.draw_lines(gc, xt2, yt2, self.get_transform()) - else: - renderer.draw_lines(gc, xt2, yt2) - - def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007年09月18日 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/patches.py 2007年09月19日 13:28:11 UTC (rev 3857) @@ -212,8 +212,8 @@ gc.set_hatch(self._hatch ) path = self.get_path() - transform = self.get_transform() - + transform = self.get_patch_transform() + self.get_transform() + renderer.draw_path(gc, path, transform, rgbFace) #renderer.close_group('patch') @@ -284,7 +284,7 @@ self.patch = patch self.props = props self.ox, self.oy = ox, oy - self._shadow_transform = transforms.Affine2D.translate(self.ox, self.oy) + self._shadow_transform = transforms.Affine2D().translate(self.ox, self.oy) self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -305,8 +305,8 @@ def get_path(self): return self.patch.get_path() - def get_transform(self): - return self._transform + self._shadow_transform + def get_patch_transform(self): + return self._shadow_transform class Rectangle(Patch): """ @@ -315,8 +315,6 @@ """ - _path = Path.unit_rectangle() - def __str__(self): return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) @@ -346,16 +344,14 @@ """ Return the vertices of the rectangle """ - # This is a "class-static" variable, so all rectangles in the plot - # will be shared (and merely have different transforms) - return self._path + return Path.unit_rectangle() # MGDTODO: Convert units # left, right = self.convert_xunits((x, x + self.width)) # bottom, top = self.convert_yunits((y, y + self.height)) - def get_transform(self): - return self._rect_transform + self._transform + def get_patch_transform(self): + return self._rect_transform def get_x(self): "Return the left coord of the rectangle" @@ -379,7 +375,8 @@ ACCEPTS: float """ - self._bbox.xmin = x + w = self._bbox.width + self._bbox.intervalx = (x, x + w) def set_y(self, y): """ @@ -387,7 +384,8 @@ ACCEPTS: float """ - self._bbox.ymin = y + h = self._bbox.height + self._bbox.intervaly = (y, y + h) def set_width(self, w): """ @@ -395,7 +393,7 @@ ACCEPTS: float """ - self._bbox.width = w + self._bbox.xmax = self._bbox.xmin + w def set_height(self, h): """ @@ -403,7 +401,7 @@ ACCEPTS: float """ - self._bbox.height = h + self._bbox.ymax = self._bbox.ymin + h def set_bounds(self, *args): """ Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月18日 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月19日 13:28:11 UTC (rev 3857) @@ -8,6 +8,8 @@ from numpy.linalg import inv from sets import Set +DEBUG = True + # MGDTODO: This creates a ton of cyclical references. We may want to # consider using weak references @@ -53,6 +55,13 @@ def __array__(self): return self.get_points() + + if DEBUG: + def invalidate(self): + points = self.get_points() + assert points[0, 0] <= points[1, 0] + assert points[0, 1] <= points[1, 1] + TransformNode.invalidate(self) # MGDTODO: Probably a more efficient ways to do this... def _get_xmin(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3856 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3856&view=rev Author: mdboom Date: 2007年09月18日 12:29:21 -0700 (2007年9月18日) Log Message: ----------- Optimize shared axes (to prevent calling set_xlim/set_ylim more than once per axes per update). Save figure at correct dpi. General cleanup and optimizations. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/backends/backend_gtkagg.py branches/transforms/lib/matplotlib/backends/backend_tkagg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/axes.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -435,6 +435,9 @@ 1 : 'log', } + _shared_x_axes = cbook.Grouper() + _shared_y_axes = cbook.Grouper() + def __str__(self): return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds) def __init__(self, fig, rect, @@ -491,6 +494,10 @@ # must be set before set_figure self._sharex = sharex self._sharey = sharey + if sharex is not None: + self._shared_x_axes.join(self, sharex) + if sharey is not None: + self._shared_y_axes.join(self, sharey) # Flag: True if some other Axes instance is sharing our x or y axis self._masterx = False self._mastery = False @@ -502,7 +509,6 @@ # this call may differ for non-sep axes, eg polar self._init_axis() - if axisbg is None: axisbg = rcParams['axes.facecolor'] self._axisbg = axisbg self._frameon = frameon @@ -616,55 +622,28 @@ #these will be updated later as data is added self._set_lim_and_transforms() - def _shared_xlim_callback(self, ax): - xmin, xmax = ax.get_xlim() - self.set_xlim(xmin, xmax, emit=False) - self.figure.canvas.draw_idle() - - def _shared_ylim_callback(self, ax): - ymin, ymax = ax.get_ylim() - self.set_ylim(ymin, ymax, emit=False) - self.figure.canvas.draw_idle() - def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ - Bbox = mtransforms.Bbox - self.viewLim = Bbox.unit() + self.viewLim = mtransforms.Bbox.unit() + self.dataLim = mtransforms.Bbox.unit() - if self._sharex is not None: - # MGDTODO: This may be doing at least one too many updates - # than necessary - self._sharex.callbacks.connect( - 'xlim_changed', self._shared_xlim_callback) - self.viewLim.intervalx = self._sharex.viewLim.intervalx - if self._sharey is not None: - self._sharey.callbacks.connect( - 'ylim_changed', self._shared_ylim_callback) - self.viewLim.intervaly = self._sharex.viewLim.intervaly - - self.dataLim = Bbox.unit() - self.transAxes = mtransforms.BboxTransform( - Bbox.unit(), self.bbox) - + mtransforms.Bbox.unit(), self.bbox) self.transData = mtransforms.BboxTransform( self.viewLim, self.bbox) def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' - # MGDTODO: This changed from returning a list to returning a Bbox - # If you get any errors with the result of this function, please - # update the calling code if original: - return copy.copy(self._originalPosition) + return self._originalPosition else: - return copy.copy(self._position) - # return [val.get() for val in self._position] + return self._position + def set_position(self, pos, which='both'): """ Set the axes position with pos = [left, bottom, width, height] @@ -699,8 +678,7 @@ self.xaxis.cla() self.yaxis.cla() - # MGDTODO - # self.dataLim.ignore(1) + self.ignore_existing_data_limits = True self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed')) if self._sharex is not None: @@ -886,7 +864,7 @@ return - l,b,w,h = self.get_position(original=True) + l,b,w,h = self.get_position(original=True).bounds box_aspect = fig_aspect * (h/w) data_ratio = box_aspect / A @@ -1152,7 +1130,7 @@ # Otherwise, it will compute the bounds of it's current data # and the data in xydata xys = npy.asarray(xys) - self.dataLim.update_numerix_xy(xys, -1) + self.update_datalim_numerix(xys[:, 0], xys[:, 1]) def update_datalim_numerix(self, x, y): @@ -1161,10 +1139,9 @@ # limits and set the bound to be the bounds of the xydata. # Otherwise, it will compute the bounds of it's current data # and the data in xydata - #print type(x), type(y) - # MGDTODO ## self.dataLim.update_numerix(x, y, -1) - self.dataLim.update_from_data(x, y) + self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits) + self.ignore_existing_data_limits = False def _get_verts_in_data_coords(self, trans, xys): if trans == self.transData: @@ -1264,9 +1241,7 @@ if not self.get_visible(): return renderer.open_group('axes') self.apply_aspect() - # MGDTODO -- this is where we can finalize all of the transforms - # self.transData.freeze() # eval the lazy objects - # self.transAxes.freeze() + if self.axison and self._frameon: self.axesPatch.draw(renderer) artists = [] @@ -1314,17 +1289,13 @@ if self.axison and self._frameon: artists.append(self.axesFrame) - # keep track of i to guarantee stable sort for python 2.2 - dsu = [ (a.zorder, i, a) for i, a in enumerate(artists) - if not a.get_animated()] + dsu = [ (a.zorder, a) for a in artists + if not a.get_animated() ] dsu.sort() - for zorder, i, a in dsu: + for zorder, a in dsu: a.draw(renderer) - # MGDTODO - # self.transData.thaw() # release the lazy objects - # self.transAxes.thaw() # release the lazy objects renderer.close_group('axes') self._cachedRenderer = renderer @@ -1509,7 +1480,6 @@ ACCEPTS: len(2) sequence of floats """ - if xmax is None and iterable(xmin): xmin,xmax = xmin @@ -1534,11 +1504,10 @@ self.viewLim.intervalx = (xmin, xmax) if emit: self.callbacks.process('xlim_changed', self) - # MGDTODO: It would be nice to do this is in the above callback list, - # but it's difficult to tell how to initialize this at the - # right time - if self._sharex: - self._sharex.set_xlim(*self.viewLim.intervalx) + # Call all of the other x-axes that are shared with this one + for other in self._shared_x_axes.get_siblings(self): + if other is not self: + other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False) return xmin, xmax @@ -1665,11 +1634,10 @@ self.viewLim.intervaly = (ymin, ymax) if emit: self.callbacks.process('ylim_changed', self) - # MGDTODO: It would be nice to do this is in the above callback list, - # but it's difficult to tell how to initialize this at the - # right time - if self._sharey: - self._sharey.set_ylim(*self.viewLim.intervaly) + # Call all of the other y-axes that are shared with this one + for other in self._shared_y_axes.get_siblings(self): + if other is not self: + other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False) return ymin, ymax Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/axis.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -1033,8 +1033,7 @@ bbox = Bbox.union(bboxes) bottom = bbox.ymin - self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi/72.0)) -# self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0)) MGDTODO + self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0)) else: if not len(bboxes2): @@ -1057,7 +1056,6 @@ bbox = Bbox.union(bboxes) bottom = bbox.ymin self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0)) -# self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO def set_ticks_position(self, position): """ @@ -1225,7 +1223,6 @@ left = bbox.xmin self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y)) - # self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y)) MGDTODO else: if not len(bboxes2): @@ -1245,7 +1242,6 @@ x,y = self.offsetText.get_position() top = self.axes.bbox.ymax self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0)) -# self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO def set_offset_position(self, position): assert position == 'left' or position == 'right' Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -18,8 +18,9 @@ """An abstract base class to handle drawing/rendering operations """ # This will cache paths across rendering instances - # Each subclass of RenderBase should define this --> - # _paths = weakref.WeakKeyDictionary() + # Each subclass of RenderBase should define this a weak-keyed + # dictionary to hold native paths + # _native_paths = weakref.WeakKeyDictionary() def __init__(self): self._texmanager = None @@ -44,7 +45,7 @@ self._native_paths[path] = native_path return native_path - def draw_path(self, gc, path, transform, rgbFace = None): + def draw_path(self, gc, path, transform, rgbFace=None): """ Handles the caching of the native path associated with the given path and calls the underlying backend's _draw_path to @@ -67,17 +68,31 @@ passed to them in draw_path. """ return path + + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + native_marker_path = self._get_cached_native_path(marker_path) + self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) - def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, - rotation): + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): """ - Draw an arc using GraphicsContext instance gcEdge, centered at x,y, - with width and height and angles from 0.0 to 360.0 - 0 degrees is at 3-o'clock - positive angles are anti-clockwise - draw rotated 'rotation' degrees anti-clockwise about x,y + This method is currently underscore hidden because the + draw_markers method is being used as a sentinel for newstyle + backend drawing - If the color rgbFace is not None, fill the arc with it. + path - a matplotlib.agg.path_storage instance + + Draw the marker specified in path with graphics context gc at + each of the locations in arrays x and y. trans is a + matplotlib.transforms.Transformation instance used to + transform x and y to display coords. It consists of an + optional nonlinear component and an affine. You can access + these two components as + + if transform.need_nonlinear(): + x,y = transform.nonlinear_only_numerix(x, y) + # the a,b,c,d,tx,ty affine which transforms x and y + vec6 = transform.as_vec6_val() + ...backend dependent affine... """ raise NotImplementedError @@ -109,30 +124,21 @@ """ return False - def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - native_marker_path = self._get_cached_native_path(marker_path) - self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) - - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): + ###################################################################### + ## OLD API IS BELOW + ## These functions no longer need to be implemented in the backends -- + ## they now perform all of their functions in terms of the new API. + + def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, + rotation): """ - This method is currently underscore hidden because the - draw_markers method is being used as a sentinel for newstyle - backend drawing + Draw an arc using GraphicsContext instance gcEdge, centered at x,y, + with width and height and angles from 0.0 to 360.0 + 0 degrees is at 3-o'clock + positive angles are anti-clockwise + draw rotated 'rotation' degrees anti-clockwise about x,y - path - a matplotlib.agg.path_storage instance - - Draw the marker specified in path with graphics context gc at - each of the locations in arrays x and y. trans is a - matplotlib.transforms.Transformation instance used to - transform x and y to display coords. It consists of an - optional nonlinear component and an affine. You can access - these two components as - - if transform.need_nonlinear(): - x,y = transform.nonlinear_only_numerix(x, y) - # the a,b,c,d,tx,ty affine which transforms x and y - vec6 = transform.as_vec6_val() - ...backend dependent affine... + If the color rgbFace is not None, fill the arc with it. """ raise NotImplementedError @@ -346,8 +352,10 @@ If rgbFace is not None, fill the rectangle with it. """ - raise NotImplementedError - + warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning) + transform = transforms.Affine2D().scale(width, height).translate(x, y) + self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace) + def draw_regpoly_collection( self, clipbox, offsets, transOffset, verts, sizes, facecolors, edgecolors, linewidths, antialiaseds): @@ -1221,8 +1229,6 @@ origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() - # MGDTODO - # self.figure.dpi.set(dpi) self.figure.dpi = dpi self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) @@ -1236,12 +1242,12 @@ orientation=orientation, **kwargs) finally: - # MGDTODO - # self.figure.dpi.set(origDPI) self.figure.dpi = origDPI self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) self.figure.set_canvas(self) + + self.draw() return result @@ -1623,8 +1629,8 @@ lims.append( (xmin, xmax, ymin, ymax) ) # Store both the original and modified positions pos.append( ( - a.get_position(True), - a.get_position() ) ) + copy.copy(a.get_position(True)), + copy.copy(a.get_position() )) ) self._views.push(lims) self._positions.push(pos) self.set_history_buttons() Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -114,9 +114,6 @@ self.height = height if __debug__: verbose.report('RendererAgg.__init__ width=%s, \ height=%s'%(width, height), 'debug-annoying') - # MGDTODO -# self._renderer = _RendererAgg(int(width), int(height), dpi.get(), -# debug=False) self._renderer = _RendererAgg(int(width), int(height), dpi, debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', @@ -136,35 +133,6 @@ def _draw_native_path(self, gc, path, transform, rgbFace): return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace) - def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): - """ - Draw an arc centered at x,y with width and height and angles - from 0.0 to 360.0 - - If rgbFace is not None, fill the rectangle with that color. gcEdge - is a GraphicsContext instance - - Currently, I'm only supporting ellipses, ie angle args are - ignored - """ - if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying') - self._renderer.draw_ellipse( - gcEdge, rgbFace, x, y, width/2, height/2, rotation) # ellipse takes radius - - - def draw_line(self, gc, x1, y1, x2, y2): - """ - x and y are equal length arrays, draw lines connecting each - point in x, y - """ - if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying') - x = npy.array([x1,x2], float) - y = npy.array([y1,y2], float) - self._renderer.draw_lines(gc, x, y) - - def draw_lines(self, gc, x, y, transform): - return self._renderer.draw_lines(gc, x, y, transform.to_values()) - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): return self._renderer.draw_markers( gc, @@ -172,18 +140,6 @@ path.vertices, path.codes, trans.get_matrix(), rgbFace) - def draw_polygon(self, *args): - return self._renderer.draw_polygon(*args) - - def draw_point(self, gc, x, y): - """ - Draw a single point at x,y - """ - if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying') - rgbFace = gc.get_rgb() - self._renderer.draw_ellipse( - gc, rgbFace, x, y, 0.5, 0.5, 0.0) - def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext @@ -192,8 +148,6 @@ 'debug-annoying') ox, oy, width, height, descent, font_image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) -# ox, oy, width, height, descent, font_image, used_characters = \ -# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO x = int(x) + ox y = int(y) - oy @@ -227,7 +181,6 @@ # (in vector space) in the above call to font.set_text. self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, angle, gc) - def get_text_width_height_descent(self, s, prop, ismath, rgb=(0,0,0)): """ get the width and height in display coords of the string s @@ -241,7 +194,7 @@ # todo: handle props size = prop.get_size_in_points() texmanager = self.get_texmanager() - Z = texmanager.get_rgba(s, size, self.dpi.get(), rgb) + Z = texmanager.get_rgba(s, size, self.dpi, rgb) m,n,tmp = Z.shape # TODO: descent of TeX text (I am imitating backend_ps here -JKS) return n, m, 0 @@ -249,8 +202,6 @@ if ismath: ox, oy, width, height, descent, fonts, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) -# ox, oy, width, height, descent, fonts, used_characters = \ -# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO return width, height, descent font = self._get_agg_font(prop) font.set_text(s, 0.0, flags=LOAD_DEFAULT) # the width and height of unrotated string @@ -265,7 +216,7 @@ # todo, handle props, angle, origins rgb = gc.get_rgb() size = prop.get_size_in_points() - dpi = self.dpi.get() + dpi = self.dpi flip = angle==90 w,h,d = self.get_text_width_height_descent(s, prop, 'TeX', rgb) @@ -306,7 +257,6 @@ 'return the canvas width and height in display coords' return self.width, self.height - def _get_agg_font(self, prop): """ Get the font for text instance t, cacheing for efficiency @@ -325,11 +275,9 @@ font.clear() size = prop.get_size_in_points() font.set_size(size, self.dpi) - # font.set_size(size, self.dpi.get()) MGDTODO return font - def points_to_pixels(self, points): """ convert point measures to pixes using dpi and the pixels per @@ -337,8 +285,6 @@ """ if __debug__: verbose.report('RendererAgg.points_to_pixels', 'debug-annoying') - # MGDTODO - # return points*self.dpi.get()/72.0 return points*self.dpi/72.0 def tostring_rgb(self): @@ -404,9 +350,7 @@ self.figure.draw(self.renderer) def get_renderer(self): - l,b,w,h = self.figure.bbox.bounds - # MGDTODO - # key = w, h, self.figure.dpi.get() + l, b, w, h = self.figure.bbox.bounds key = w, h, self.figure.dpi try: self._lastKey, self.renderer except AttributeError: need_new_renderer = True Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -60,7 +60,6 @@ w,h = widget.window.get_size() if w==1 or h==1: return # empty fig - # dpival = self.figure.dpi.get() MGDTODO # compute desired figure size in inches dpival = self.figure.dpi winch = w/dpival Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -175,7 +175,6 @@ self._resize_callback(event) # compute desired figure size in inches - # dpival = self.figure.dpi.get() MGDTODO dpival = self.figure.dpi winch = width/dpival hinch = height/dpival Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/cbook.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -955,6 +955,84 @@ outstream.write("Examining: %r\n" % (obj,)) recurse(obj, obj, { }, []) +class Grouper(object): + """ + This class provides a lightweight way to group arbitrary objects + together into disjoint sets when a full-blown graph data structure + would be overkill. + + Objects can be joined using .join(), tested for connectedness + using .joined(), and all disjoint sets can be retreived using + .get(). + + The objects being joined must be hashable. + + For example: + + >>> g = grouper.Grouper() + >>> g.join('a', 'b') + >>> g.join('b', 'c') + >>> g.join('d', 'e') + >>> list(g.get()) + [['a', 'b', 'c'], ['d', 'e']] + >>> g.joined('a', 'b') + True + >>> g.joined('a', 'c') + True + >>> g.joined('a', 'd') + False""" + def __init__(self, init=[]): + mapping = self._mapping = {} + for x in init: + mapping[x] = [x] + + def join(self, a, *args): + """ + Join given arguments into the same set. + Accepts one or more arguments. + """ + mapping = self._mapping + set_a = mapping.setdefault(a, [a]) + + for arg in args: + set_b = mapping.get(arg) + if set_b is None: + set_a.append(arg) + mapping[arg] = set_a + elif set_b is not set_a: + if len(set_b) > len(set_a): + set_a, set_b = set_b, set_a + set_a.extend(set_b) + for elem in set_b: + mapping[elem] = set_a + + def joined(self, a, b): + """ + Returns True if a and b are members of the same set. + """ + mapping = self._mapping + try: + return mapping[a] is mapping[b] + except KeyError: + return False + + def __iter__(self): + """ + Returns an iterator returning each of the disjoint sets as a list. + """ + seen = set() + for elem, group in self._mapping.iteritems(): + if elem not in seen: + yield group + seen.update(group) + + def get_siblings(self, a): + """ + Returns all of the items joined with the given item, including + itself. + """ + return self._mapping.get(a, [a]) + if __name__=='__main__': assert( allequal([1,1,1]) ) assert(not allequal([1,1,0]) ) Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/figure.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -161,10 +161,8 @@ def _get_dpi(self): return self._dpi def _set_dpi(self, dpi): - print "setting dpi" self._dpi = dpi self._dpi_scale_trans.clear().scale(dpi, dpi) - print self._dpi_scale_trans dpi = property(_get_dpi, _set_dpi) def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'): @@ -178,10 +176,7 @@ bottom : the bottom of the subplots for subplots_adjust rotation: the rotation of the xtick labels ha : the horizontal alignment of the xticklabels - - """ - for ax in self.get_axes(): if not hasattr(ax, 'is_last_row'): raise RuntimeError('Axes must be subplot instances; found %s'%type(ax)) @@ -333,11 +328,8 @@ dpival = self.dpi self.bbox_inches.max = w, h - # self.figwidth.set(w) MGDTODO - # self.figheight.set(h) if forward: - # dpival = self.dpi.get() dpival = self.dpi canvasw = w*dpival canvash = h*dpival @@ -347,7 +339,6 @@ def get_size_inches(self): return self.bbox_inches.max - # return self.figwidth.get(), self.figheight.get() MGDTODO def get_edgecolor(self): 'Get the edge color of the Figure rectangle' @@ -360,7 +351,6 @@ def get_figwidth(self): 'Return the figwidth as a float' return self.bbox_inches.xmax - # return self.figwidth.get() MGDTODO def get_figheight(self): 'Return the figheight as a float' @@ -369,7 +359,6 @@ def get_dpi(self): 'Return the dpi as a float' return self.dpi - # return self.dpi.get() MGDTODO def get_frameon(self): 'get the boolean indicating frameon' @@ -397,7 +386,6 @@ ACCEPTS: float """ - # self.dpi.set(val) MGDTODO self.dpi = val def set_figwidth(self, val): @@ -406,7 +394,6 @@ ACCEPTS: float """ - # self.figwidth.set(val) MGDTODO self.bbox_inches.xmax = val def set_figheight(self, val): @@ -415,7 +402,6 @@ ACCEPTS: float """ - # MGDTODO (set()) self.bbox_inches.ymax = val def set_frameon(self, b): @@ -598,8 +584,6 @@ #print 'figure draw' if not self.get_visible(): return renderer.open_group('figure') - # MGDTODO - # self.transFigure.freeze() # eval the lazy objects if self.frameon: self.figurePatch.draw(renderer) @@ -633,8 +617,6 @@ for legend in self.legends: legend.draw(renderer) - # MGDTODO - # self.transFigure.thaw() # release the lazy objects renderer.close_group('figure') self._cachedRenderer = renderer Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/legend.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -21,7 +21,7 @@ up the legend """ from __future__ import division -import sys, warnings +import copy, sys, warnings import numpy as npy @@ -558,9 +558,7 @@ handle.set_height(h/2) # Set the data for the legend patch - # MGDTODO: This copy may no longer be needed now that Bboxes are - # essentially immutable - bbox = self._get_handle_text_bbox(renderer).copy() + bbox = copy.copy(self._get_handle_text_bbox(renderer)) bbox = bbox.scaled(1 + self.pad, 1 + self.pad) l,b,w,h = bbox.get_bounds() Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -352,32 +352,18 @@ self._picker = p def get_window_extent(self, renderer): - self._newstyle = hasattr(renderer, 'draw_markers') - if self._newstyle: - x = self._x - y = self._y - else: - x, y = self._get_plottable() + xys = self.get_transform()(self._xys) - # MGDTODO: Put this in a single Nx2 array, rather than these - # separate ones - #### Conversion code - a = npy.vstack((x, y)).swapaxes(0, 1) - #### - x, y = self.get_transform()(a) - print "get_window_extent", self.get_transform() - - #x, y = self.get_transform().seq_x_y(x, y) + x = xys[:, 0] + y = xys[:, 1] + left = x.min() + bottom = y.min() + width = x.max() - left + height = y.max() - bottom - left = min(x) - bottom = min(y) - width = max(x) - left - height = max(y) - bottom - # correct for marker size, if any if self._marker is not None: - ms = self._markersize/72.0*self.figure.dpi - # ms = self._markersize/72.0*self.figure.dpi.get() MGDTODO + ms = self._markersize / 72.0 * self.figure.dpi left -= ms/2 bottom -= ms/2 width += ms @@ -411,6 +397,7 @@ def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units + # MGDTODO: Deal with units x = ma.asarray(self.convert_xunits(self._xorig), float) y = ma.asarray(self.convert_yunits(self._yorig), float) @@ -435,15 +422,15 @@ else: self._segments = None - self._x = npy.asarray(x, float) - self._y = npy.asarray(y, float) - self._path = Path(npy.vstack((self._x, self._y)).transpose(), - closed=False) + self._xy = npy.vstack((npy.asarray(x, npy.float_), + npy.asarray(y, npy.float_))).transpose() + self._x = self._xy[:, 0] + self._y = self._xy[:, 1] + self._path = Path(self._xy, closed=False) self._logcache = None - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月18日 19:29:21 UTC (rev 3856) @@ -19,9 +19,9 @@ self._parents = Set() def invalidate(self): - if not self._do_invalidation(): - for parent in self._parents: - parent.invalidate() + self._do_invalidation() + for parent in self._parents: + parent.invalidate() def _do_invalidation(self): return False @@ -187,14 +187,16 @@ return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ - # JDH: the update method will update the box limits from the - # existing limits and the new data; it appears here you are just - # using the new data. We use an "ignore" flag to specify whether - # you want to include the existing data or not in the update def update_from_data(self, x, y, ignore=True): - self._points = npy.array( - [[x.min(), y.min()], [x.max(), y.max()]], - npy.float_) + if ignore: + self._points = npy.array( + [[x.min(), y.min()], [x.max(), y.max()]], + npy.float_) + else: + self._points = npy.array( + [[min(x.min(), self.xmin), min(y.min(), self.ymin)], + [max(x.max(), self.xmax), max(y.max(), self.ymax)]], + npy.float_) self.invalidate() # MGDTODO: Probably a more efficient ways to do this... @@ -409,9 +411,7 @@ return self.get_matrix() def _do_invalidation(self): - result = self._inverted is None self._inverted = None - return result #@staticmethod def _concat(a, b): @@ -494,6 +494,7 @@ if matrix is None: matrix = npy.identity(3) else: + matrix = npy.asarray(matrix, npy.float_) assert matrix.shape == (3, 3) self._mtx = matrix self._inverted = None @@ -629,8 +630,6 @@ if self._mtx is not None: self._mtx = None Affine2DBase._do_invalidation(self) - return False - return True def is_separable(self): return True @@ -684,7 +683,7 @@ def _do_invalidation(self): self._mtx = None - Affine2DBase._do_invalidation(self) + return Affine2DBase._do_invalidation(self) def get_matrix(self): if self._mtx is None: @@ -718,8 +717,6 @@ if self._mtx is not None: self._mtx = None Affine2DBase._do_invalidation(self) - return False - return True def is_separable(self): return True Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月18日 16:21:37 UTC (rev 3855) +++ branches/transforms/src/_backend_agg.cpp 2007年09月18日 19:29:21 UTC (rev 3856) @@ -456,7 +456,7 @@ //return the agg::rect for bbox, flipping y PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2); - if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) + if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) throw Py::TypeError ("Expected a Bbox object."); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
Revision: 3855 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3855&view=rev Author: mdboom Date: 2007年09月18日 09:21:37 -0700 (2007年9月18日) Log Message: ----------- More code using new transformation framework. Lots of dead code removed from backend_agg.cpp/h Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/lib/matplotlib/type1font.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -39,8 +39,7 @@ def _get_cached_native_path(self, path): native_path = self._native_paths.get(path) if native_path is None: - import matplotlib.patches - print "CACHE MISS", path + # print "CACHE MISS", path native_path = self.convert_to_native_path(path) self._native_paths[path] = native_path return native_path Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -121,13 +121,9 @@ debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', 'debug-annoying') - # self.draw_polygon = self._renderer.draw_polygon - self.draw_rectangle = self._renderer.draw_rectangle - # MGDTODO -- remove these lines - # self.draw_lines = self._renderer.draw_lines - # self.draw_markers = self._renderer.draw_markers - self.draw_image = self._renderer.draw_image + self.convert_to_native_path = self._renderer.convert_to_native_path + self.draw_image = self._renderer.draw_image self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region self.mathtext_parser = MathTextParser('Agg') @@ -137,12 +133,9 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') - def convert_to_native_path(self, path): - return self._renderer.convert_to_native_path(path.vertices, path.codes) - - def _draw_native_path(self, gc, native_path, transform, rgbFace): - return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace) - + def _draw_native_path(self, gc, path, transform, rgbFace): + return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace) + def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): """ Draw an arc centered at x,y with width and height and angles @@ -175,8 +168,8 @@ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): return self._renderer.draw_markers( gc, - native_marker_path, marker_trans.to_values(), - path.vertices, path.codes, trans.to_values(), + native_marker_path, marker_trans.get_matrix(), + path.vertices, path.codes, trans.get_matrix(), rgbFace) def draw_polygon(self, *args): Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -712,6 +712,7 @@ def _draw_nothing(self, renderer, gc, path): pass + def _draw_steps(self, renderer, gc, xt, yt): # MGDTODO: This is a quirky one. The step-plotting part # should probably be moved to where the path is generated @@ -729,6 +730,7 @@ else: renderer.draw_lines(gc, xt2, yt2) + def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) @@ -753,8 +755,15 @@ def _draw_point(self, renderer, gc, path): - self._draw_circle(renderer, gc, path, point = True) + w = renderer.points_to_pixels(self._markersize) * \ + self._point_size_reduction * 0.5 + rgbFace = self._get_rgb_face() + transform = Affine2D().scale(w) + renderer.draw_markers( + gc, Path.unit_circle(), transform, path, self.get_transform(), + rgbFace) + def _draw_pixel(self, renderer, gc, path): rgbFace = self._get_rgb_face() transform = Affine2D().translate(-0.5, -0.5) @@ -762,12 +771,8 @@ path, self.get_transform(), rgbFace) - def _draw_circle(self, renderer, gc, path, point=False): - w = renderer.points_to_pixels(self._markersize) - if point: - w *= self._point_size_reduction - w *= 0.5 - + def _draw_circle(self, renderer, gc, path): + w = renderer.points_to_pixels(self._markersize) * 0.5 rgbFace = self._get_rgb_face() transform = Affine2D().scale(w, w) renderer.draw_markers( @@ -826,7 +831,8 @@ def _draw_thin_diamond(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset) + transform = Affine2D().translate(0.5, 0.5) \ + .rotate_deg(45).scale(offset * 0.8, offset) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_rectangle(), transform, path, self.get_transform(), rgbFace) @@ -840,7 +846,7 @@ path, self.get_transform(), rgbFace) - def _draw_hexagon1(self, renderer, gc, path, point=False): + def _draw_hexagon1(self, renderer, gc, path): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) rgbFace = self._get_rgb_face() @@ -848,50 +854,44 @@ path, self.get_transform(), rgbFace) - def _draw_hexagon2(self, renderer, gc, xt, yt): + def _draw_hexagon2(self, renderer, gc, path): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset).rotate_deg(30) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, path, self.get_transform(), rgbFace) - - def _draw_vline(self, renderer, gc, xt, yt): + + _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]], closed=False) + def _draw_vline(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, -offset) - path.line_to(0, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y-offset, x, y+offset) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._line_marker_path, transform, + path, self.get_transform()) - def _draw_hline(self, renderer, gc, xt, yt): + def _draw_hline(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to(offset, 0) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x+offset, y) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._line_marker_path, transform, + path, self.get_transform()) + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) def _draw_tickleft(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - marker_transform = Affine2D().scale(offset, 1.0) + marker_transform = Affine2D().scale(-offset, 1.0) renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, path, self.get_transform()) + def _draw_tickright(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - marker_transform = Affine2D().scale(-offset, 1.0) + marker_transform = Affine2D().scale(offset, 1.0) renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, path, self.get_transform()) + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) def _draw_tickup(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) @@ -899,174 +899,99 @@ renderer.draw_markers(gc, self._tickvert_path, marker_transform, path, self.get_transform()) + def _draw_tickdown(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, -offset) renderer.draw_markers(gc, self._tickvert_path, marker_transform, path, self.get_transform()) - def _draw_plus(self, renderer, gc, xt, yt): + + _plus_path = Path([[-1.0, 0.0], [1.0, 0.0], + [0.0, -1.0], [0.0, 1.0]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) + def _draw_plus(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._plus_path, transform, + path, self.get_transform()) - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to( offset, 0) - path.move_to( 0, -offset) - path.line_to( 0, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x+offset, y) - renderer.draw_line(gc, x, y-offset, x, y+offset) - def _draw_tri_down(self, renderer, gc, xt, yt): + _tri_path = Path([[0.0, 0.0], [0.0, -1.0], + [0.0, 0.0], [0.8, 0.5], + [0.0, 0.0], [-0.8, 0.5]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) + def _draw_tri_down(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(0, -offset) - path.move_to(0, 0) - path.line_to(offset1, offset2) - path.move_to(0, 0) - path.line_to(-offset1, offset2) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x, y-offset) - renderer.draw_line(gc, x, y, x+offset1, y+offset2) - renderer.draw_line(gc, x, y, x-offset1, y+offset2) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_up(self, renderer, gc, xt, yt): + + def _draw_tri_up(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(0, offset) - path.move_to(0, 0) - path.line_to(offset1, -offset2) - path.move_to(0, 0) - path.line_to(-offset1, -offset2) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x, y+offset) - renderer.draw_line(gc, x, y, x+offset1, y-offset2) - renderer.draw_line(gc, x, y, x-offset1, y-offset2) + transform = Affine2D().scale(offset).rotate_deg(180) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_left(self, renderer, gc, xt, yt): - offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(-offset, 0) - path.move_to(0, 0) - path.line_to(offset2, offset1) - path.move_to(0, 0) - path.line_to(offset2, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x-offset, y) - renderer.draw_line(gc, x, y, x+offset2, y+offset1) - renderer.draw_line(gc, x, y, x+offset2, y-offset1) + + def _draw_tri_left(self, renderer, gc, path): + offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_right(self, renderer, gc, xt, yt): - offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(offset, 0) - path.move_to(0, 0) - path.line_to(-offset2, offset1) - path.move_to(0, 0) - path.line_to(-offset2, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x+offset, y) - renderer.draw_line(gc, x, y, x-offset2, y+offset1) - renderer.draw_line(gc, x, y, x-offset2, y-offset1) + + def _draw_tri_right(self, renderer, gc, path): + offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(270) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_caretdown(self, renderer, gc, xt, yt): + + _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]], closed=False) + def _draw_caretdown(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, offset1) - path.line_to(0, 0) - path.line_to(+offset, offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y+offset1, x, y) - renderer.draw_line(gc, x, y, x+offset, y+offset1) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretup(self, renderer, gc, xt, yt): + + def _draw_caretup(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, -offset1) - path.line_to(0, 0) - path.line_to(+offset, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y-offset1, x, y) - renderer.draw_line(gc, x, y, x+offset, y-offset1) + transform = Affine2D().scale(offset).rotate_deg(180) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretleft(self, renderer, gc, xt, yt): + + def _draw_caretleft(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(offset1, -offset) - path.line_to(0, 0) - path.line_to(offset1, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x+offset1, y-offset, x, y) - renderer.draw_line(gc, x, y, x+offset1, y+offset) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretright(self, renderer, gc, xt, yt): + + def _draw_caretright(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset1, -offset) - path.line_to(0, 0) - path.line_to(-offset1, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset1, y-offset, x, y) - renderer.draw_line(gc, x, y, x-offset1, y+offset) + transform = Affine2D().scale(offset).rotate_deg(270) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) + + _x_path = Path([[-1.0, -1.0], [1.0, 1.0], + [-1.0, 1.0], [1.0, -1.0]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) def _draw_x(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._x_path, transform, + path, self.get_transform()) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, -offset) - path.line_to(offset, offset) - path.move_to(-offset, offset) - path.line_to(offset, -offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y-offset, x+offset, y+offset) - renderer.draw_line(gc, x-offset, y+offset, x+offset, y-offset) - + def update_from(self, other): 'copy properties from other to self' Artist.update_from(self, other) Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/path.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -50,6 +50,9 @@ i += NUM_VERTICES[code] assert i == len(self.vertices) + def __repr__(self): + return "Path(%s, %s)" % (self.vertices, self.codes) + def _get_codes(self): return self._codes codes = property(_get_codes) Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/transforms.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -88,11 +88,13 @@ intervaly = property(_get_intervaly) def _get_width(self): - return self.xmax - self.xmin + points = self.get_points() + return points[1, 0] - points[0, 0] width = property(_get_width) def _get_height(self): - return self.ymax - self.ymin + points = self.get_points() + return points[1, 1] - points[0, 1] height = property(_get_height) def _get_bounds(self): Modified: branches/transforms/lib/matplotlib/type1font.py =================================================================== --- branches/transforms/lib/matplotlib/type1font.py 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/type1font.py 2007年09月18日 16:21:37 UTC (rev 3855) @@ -1,15 +1,15 @@ """ A class representing a Type 1 font. -This version merely allows reading in pfa and pfb files, stores the -data in pfa format, and allows reading the parts of the data in a -format suitable for embedding in pdf files. A more complete class -might support subsetting. +This version merely reads pfa and pfb files and splits them for +embedding in pdf files. There is no support yet for subsetting or +anything like that. -Usage: font = Type1Font(filename) - somefile.write(font.data) # writes out font in pfa format - len1, len2, len3 = font.lengths() # needed for pdf embedding +Usage (subject to change): + font = Type1Font(filename) + clear_part, encrypted_part, finale = font.parts + Source: Adobe Technical Note #5040, Supporting Downloadable PostScript Language Fonts. @@ -32,8 +32,7 @@ def _read(self, file): rawdata = file.read() if not rawdata.startswith(chr(128)): - self.data = rawdata - return + return rawdata data = '' while len(rawdata) > 0: @@ -101,4 +100,6 @@ if __name__ == '__main__': import sys font = Type1Font(sys.argv[1]) - sys.stdout.write(font.data) + parts = font.parts + print len(parts[0]), len(parts[1]), len(parts[2]) + Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月17日 13:41:38 UTC (rev 3854) +++ branches/transforms/src/_backend_agg.cpp 2007年09月18日 16:21:37 UTC (rev 3855) @@ -10,7 +10,6 @@ #include <time.h> #include <algorithm> - #include "agg_conv_transform.h" #include "agg_conv_curve.h" #include "agg_scanline_storage_aa.h" @@ -43,28 +42,48 @@ #define M_PI_2 1.57079632679489661923 #endif -agg::trans_affine py_sequence_to_agg_transformation_matrix(const Py::Object& obj) { - Py::SeqBase<Py::Float> seq; +/** A helper function to convert from a Numpy affine transformation matrix + * to an agg::trans_affine. + */ +agg::trans_affine py_to_agg_transformation_matrix(const Py::Object& obj) { + PyArrayObject* matrix = NULL; + + double a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0; + try { - seq = obj; - } catch(...) { - throw Py::ValueError("Transformation matrix must be given as a 6-element list."); - } + matrix = (PyArrayObject*) PyArray_ContiguousFromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!matrix || matrix->nd != 2 || matrix->dimensions[0] != 3 || matrix->dimensions[1] != 3) { + throw Py::ValueError("Invalid affine transformation matrix."); + } - if (seq.size() != 6) { - throw Py::ValueError("Transformation matrix must be given as a 6-element list."); + size_t stride0 = matrix->strides[0]; + size_t stride1 = matrix->strides[1]; + char* row0 = matrix->data; + char* row1 = row0 + stride0; + + a = *(double*)(row0); + row0 += stride1; + c = *(double*)(row0); + row0 += stride1; + e = *(double*)(row0); + + b = *(double*)(row1); + row1 += stride1; + d = *(double*)(row1); + row1 += stride1; + f = *(double*)(row1); + } catch (...) { + Py_XDECREF(matrix); } - return agg::trans_affine - (Py::Float(seq[0]), - Py::Float(seq[1]), - Py::Float(seq[2]), - Py::Float(seq[3]), - Py::Float(seq[4]), - Py::Float(seq[5])); + Py_XDECREF(matrix); + + return agg::trans_affine(a, b, c, d, e, f); } -// MGDTODO: Implement this as a nice iterator +/** Helper function to get the next vertex in a Numpy array of vertices. + * Will generally be used through the GET_NEXT_VERTEX macro. + */ inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, double& x, double& y, size_t next_vertex_stride, @@ -78,12 +97,18 @@ #define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) +Py::Object BufferRegion::to_string(const Py::Tuple &args) { + + // owned=true to prevent memory leak + return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); +} + + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), Ndash(0), dashOffset(0.0), dasha(NULL) { - _VERBOSE("GCAgg::GCAgg"); linewidth = points_to_pixels ( gc.getAttr("_linewidth") ) ; alpha = Py::Float( gc.getAttr("_alpha") ); @@ -100,7 +125,6 @@ GCAgg::_set_antialiased(const Py::Object& gc) { _VERBOSE("GCAgg::antialiased"); isaa = Py::Int( gc.getAttr( "_antialiased") ); - } agg::rgba @@ -123,13 +147,12 @@ return p * dpi/72.0; } - void GCAgg::_set_linecap(const Py::Object& gc) { _VERBOSE("GCAgg::_set_linecap"); std::string capstyle = Py::String( gc.getAttr( "_capstyle" ) ); - + if (capstyle=="butt") cap = agg::butt_cap; else if (capstyle=="round") @@ -138,7 +161,6 @@ cap = agg::square_cap; else throw Py::ValueError(Printf("GC _capstyle attribute must be one of butt, round, projecting; found %s", capstyle.c_str()).str()); - } void @@ -155,7 +177,6 @@ join = agg::bevel_join; else throw Py::ValueError(Printf("GC _joinstyle attribute must be one of butt, round, projecting; found %s", joinstyle.c_str()).str()); - } void @@ -193,7 +214,7 @@ } } - +// MGDTODO: Convert directly from Bbox object (numpy) void GCAgg::_set_clip_rectangle( const Py::Object& gc) { //set the clip rectangle from the gc @@ -204,7 +225,7 @@ cliprect = NULL; Py::Object o ( gc.getAttr( "_cliprect" ) ); - if (o.ptr()==Py_None) { + if (o.ptr() == Py_None) { return; } @@ -229,38 +250,18 @@ _VERBOSE("GCAgg::_set_clip_path"); - delete clippath; + Py_XINCREF(clippath); clippath = NULL; - Py::Object o = gc.getAttr( "_clippath" ); + Py::Object o = gc.getAttr("_clippath"); if (o.ptr()==Py_None) { return; } - agg::path_storage *tmppath; - swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *"); - assert(descr); - if (SWIG_ConvertPtr(o.ptr(),(void **)(&tmppath), descr, 0) == -1) { - throw Py::TypeError("Could not convert gc path_storage"); - } - - tmppath->rewind(0); - clippath = new agg::path_storage(); - clippath->copy_from(*tmppath); - clippath->rewind(0); - tmppath->rewind(0); + clippath = new PathAgg(o); } -Py::Object BufferRegion::to_string(const Py::Tuple &args) { - - // owned=true to prevent memory leak - return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); -} - - - - const size_t RendererAgg::PIXELS_PER_INCH(96); @@ -311,19 +312,14 @@ void -RendererAgg::set_clipbox_rasterizer( double *cliprect) { +RendererAgg::set_clipbox_rasterizer(double *cliprect) { //set the clip rectangle from the gc _VERBOSE("RendererAgg::set_clipbox_rasterizer"); - theRasterizer->reset_clipping(); rendererBase->reset_clipping(true); - //if (cliprect==NULL) { - // theRasterizer->reset_clipping(); - // rendererBase->reset_clipping(true); - //} if (cliprect!=NULL) { double l = cliprect[0] ; @@ -355,273 +351,10 @@ } -// MGDTODO: Remove this method (it has been conglomerated into draw_path -template <class VS> -void -RendererAgg::_fill_and_stroke(VS& path, - const GCAgg& gc, - const facepair_t& face, - bool curvy) { - typedef agg::conv_curve<VS> curve_t; - - //bool isclippath(gc.clippath!=NULL); - //if (isclippath) _process_alpha_mask(gc); - - if (face.first) { - rendererAA->color(face.second); - if (curvy) { - curve_t curve(path); - theRasterizer->add_path(curve); - } - else - theRasterizer->add_path(path); - - /* - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - */ - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - - //now stroke the edge - if (gc.linewidth) { - if (curvy) { - curve_t curve(path); - agg::conv_stroke<curve_t> stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - else { - agg::conv_stroke<VS> stroke(path); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - - - /* - if ( gc.isaa ) { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - } - else { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else{ - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } - - */ - - if ( gc.isaa ) { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - else { - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } - - -} - -Py::Object -RendererAgg::draw_rectangle(const Py::Tuple & args) { - _VERBOSE("RendererAgg::draw_rectangle"); - args.verify_length(6); - - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - - double l = Py::Float( args[2] ); - double b = Py::Float( args[3] ); - double w = Py::Float( args[4] ); - double h = Py::Float( args[5] ); - - b = height - (b+h); - double r = l + w; - double t = b + h; - - //snapto pixel centers - l = (int)l + 0.5; - b = (int)b + 0.5; - r = (int)r + 0.5; - t = (int)t + 0.5; - - - set_clipbox_rasterizer(gc.cliprect); - - agg::path_storage path; - - - path.move_to(l, t); - path.line_to(r, t); - path.line_to(r, b); - path.line_to(l, b); - path.close_polygon(); - - _fill_and_stroke(path, gc, face, false); - - return Py::Object(); - -} - -Py::Object -RendererAgg::draw_ellipse(const Py::Tuple& args) { - _VERBOSE("RendererAgg::draw_ellipse"); - args.verify_length(7); - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - double x = Py::Float( args[2] ); - double y = Py::Float( args[3] ); - double w = Py::Float( args[4] ); - double h = Py::Float( args[5] ); - double rot = Py::Float( args[6] ); - - double r; // rot in radians - - set_clipbox_rasterizer(gc.cliprect); - - // Approximate the ellipse with 4 bezier paths - agg::path_storage path; - if (rot == 0.0) // simple case - { - path.move_to(x, height-(y+h)); - path.arc_to(w, h, 0.0, false, true, x+w, height-y); - path.arc_to(w, h, 0.0, false, true, x, height-(y-h)); - path.arc_to(w, h, 0.0, false, true, x-w, height-y); - path.arc_to(w, h, 0.0, false, true, x, height-(y+h)); - path.close_polygon(); - } - else // rotate by hand :( - { - // deg to rad - r = rot * (M_PI/180.0); - path.move_to( x+(cos(r)*w), height-(y+(sin(r)*w))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2*3)*h), height-(y+(sin(r+M_PI_2*3)*h))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI)*w), height-(y+(sin(r+M_PI)*w))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2)*h), height-(y+(sin(r+M_PI_2)*h))); - path.arc_to(w, h, -r, false, true, x+(cos(r)*w), height-(y+(sin(r)*w))); - path.close_polygon(); - } - - _fill_and_stroke(path, gc, face); - return Py::Object(); - -} - -Py::Object -RendererAgg::draw_polygon(const Py::Tuple& args) { - _VERBOSE("RendererAgg::draw_polygon"); - - args.verify_length(3); - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - Py::SeqBase<Py::Object> points( args[2] ); - - set_clipbox_rasterizer(gc.cliprect); - - size_t Npoints = points.length(); - if (Npoints<=0) - return Py::Object(); - - - // dump the x.y vertices into a double array for faster look ahead - // and behind access - double *xs = new double[Npoints]; - double *ys = new double[Npoints]; - - for (size_t i=0; i<Npoints; i++) { - Py::SeqBase<Py::Object> xy(points[i]); - xy = Py::Tuple(points[i]); - xs[i] = Py::Float(xy[0]); - ys[i] = Py::Float(xy[1]); - ys[i] = height - ys[i]; - } - - - - agg::path_storage path; - for (size_t j=0; j<Npoints; j++) { - - double x = xs[j]; - double y = ys[j]; - - //snapto pixel centers - x = (int)x + 0.5; - y = (int)y + 0.5; - - if (j==0) path.move_to(x,y); - else path.line_to(x,y); - } - path.close_polygon(); - - _fill_and_stroke(path, gc, face, false); - - delete [] xs; - delete [] ys; - - _VERBOSE("RendererAgg::draw_polygon DONE"); - return Py::Object(); - -} - - - SnapData SafeSnap::snap (const float& x, const float& y) { xsnap = (int)x + 0.5; ysnap = (int)y + 0.5; - - if ( first || ( (xsnap!=lastxsnap) || (ysnap!=lastysnap) ) ) { lastxsnap = xsnap; @@ -690,9 +423,6 @@ rb.copy_from(*renderingBuffer, &r, -r.x1, -r.y1); BufferRegion* reg = new BufferRegion(buf, r, true); return Py::asObject(reg); - - - } Py::Object @@ -715,25 +445,20 @@ rendererBase->copy_from(rbuf, 0, region->rect.x1, region->rect.y1); return Py::Object(); - - - } - +/** + * Helper function to convert a Python Bbox object to an agg rectangle + */ template<class T> agg::rect_base<T> RendererAgg::bbox_to_rect(const Py::Object& o) { //return the agg::rect for bbox, flipping y PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2); - if (!bbox) + if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) throw Py::TypeError ("Expected a Bbox object."); - - if (bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) - throw Py::TypeError - ("Expected a Bbox object."); double l = bbox->data[0]; double b = bbox->data[1]; @@ -805,469 +530,8 @@ return numIntersect; } -void RendererAgg::DrawQuadMesh(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[]) -{ - /* draw each quadrilateral */ - // agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase); - int i = 0; - int j = 0; - int k = 0; - double xs[4]; - double ys[4]; - int col[4]; - int numCol; - double ymin; - int firstRow; - double ymax; - int lastRow; - for(i=0; i < meshHeight; i++) - { - for(j=0; j < meshWidth; j++) - { - //currTime = clock(); - xs[0] = xCoords[(i * (meshWidth + 1)) + j]; - ys[0] = yCoords[(i * (meshWidth + 1)) + j]; - xs[1] = xCoords[(i * (meshWidth + 1)) + j+1]; - ys[1] = yCoords[(i * (meshWidth + 1)) + j+1]; - xs[3] = xCoords[((i+1) * (meshWidth + 1)) + j]; - ys[3] = yCoords[((i+1) * (meshWidth + 1)) + j]; - xs[2] = xCoords[((i+1) * (meshWidth + 1)) + j+1]; - ys[2] = yCoords[((i+1) * (meshWidth + 1)) + j+1]; - ymin = std::min(std::min(std::min(ys[0], ys[1]), ys[2]), ys[3]); - ymax = std::max(std::max(std::max(ys[0], ys[1]), ys[2]), ys[3]); - firstRow = (int)(ymin); - lastRow = (int)(ymax); - //timer1 += (clock() - currTime); - //currTime = clock(); - //timer2 += (clock() - currTime); - //currTime = clock(); - for(k = firstRow; k <= lastRow; k++) - { - numCol = inPolygon(k, xs, ys, col); - if (numCol >= 2) rendererBase->copy_hline(col[0], k, col[1] - 1, colorArray[(i * meshWidth) + j]); - if (numCol == 4) rendererBase->copy_hline(col[2], k, col[3] - 1, colorArray[(i * meshWidth) + j]); - } - } - } - return; -} -void RendererAgg::DrawQuadMeshEdges(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[]) -{ - int i, j; - agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase); - agg::rgba8 lc(0, 0, 0, 32); - lineRen.line_color(lc); - /* show the vertical edges */ - for(i=0; i <= meshWidth; i++) - { - lineRen.move_to((int)(256.0 * (xCoords[i])), (int)(256.0 * (yCoords[i]))); - for(j=1; j <= meshHeight; j++) - lineRen.line_to((int)(256.0 *(xCoords[(j * (meshWidth + 1))+i])), (int)(256.0 * (yCoords[(j * (meshWidth + 1))+i]))); - } - /* show the horizontal edges */ - for(i=0; i <= meshHeight; i++) - { - lineRen.move_to((int)(256.0 * (xCoords[i * (meshWidth + 1)])), (int)(256.0 * (yCoords[i * (meshWidth + 1)]))); - for(j=1; j <= meshWidth; j++) - lineRen.line_to((int)(256.0 * (xCoords[(i * (meshWidth + 1))+j])), (int)(256.0 * (yCoords[(i * (meshWidth + 1))+j]))); - } -} - - - Py::Object -RendererAgg::draw_lines(const Py::Tuple& args) { - - _VERBOSE("RendererAgg::draw_lines"); - args.verify_length(4); - - Py::Object xo = args[1]; - Py::Object yo = args[2]; - - PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1); - - if (xa==NULL) - throw Py::TypeError("RendererAgg::draw_lines expected numerix array"); - - - PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1); - - if (ya==NULL) - throw Py::TypeError("RendererAgg::draw_lines expected numerix array"); - - - size_t Nx = xa->dimensions[0]; - size_t Ny = ya->dimensions[0]; - - if (Nx!=Ny) - throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str()); - - // call gc with snapto==True if line len is 2 to fix grid line - // problem - bool snapto = false; - if (Nx==2) { - // disable subpiel rendering for len(2) horizontal or vertical - // lines - double x0 = *(double *)(xa->data + 0*xa->strides[0]); - double x1 = *(double *)(xa->data + 1*xa->strides[0]); - double y0 = *(double *)(ya->data + 0*ya->strides[0]); - double y1 = *(double *)(ya->data + 1*ya->strides[0]); - snapto = (x0==x1) || (y0==y1); - - } - GCAgg gc = GCAgg(args[0], dpi, snapto); - - set_clipbox_rasterizer(gc.cliprect); - //path_t transpath(path, xytrans); - _process_alpha_mask(gc); - - agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[3]); - - agg::path_storage path; - - // MGDTODO - bool needNonlinear = false; - // mpltransform->need_nonlinear_api(); - - double thisx(0.0), thisy(0.0); - double origdx(0.0), origdy(0.0), origdNorm2(0); - bool moveto = true; - double heightd = height; - - double lastx(0), lasty(0); - double lastWrittenx(0), lastWritteny(0); - bool clipped = false; - - bool haveMin = false, lastMax = true; - double dnorm2Min(0), dnorm2Max(0); - double maxX(0), maxY(0), minX(0), minY(0); - - double totdx, totdy, totdot; - double paradx, parady, paradNorm2; - double perpdx, perpdy, perpdNorm2; - - int counter = 0; - //idea: we can skip drawing many lines: lines < 1 pixel in length, lines - //outside of the drawing area, and we can combine sequential parallel lines - //into a single line instead of redrawing lines over the same points. - //The loop below works a bit like a state machine, where what it does depends - //on what it did in the last looping. To test whether sequential lines - //are close to parallel, I calculate the distance moved perpendicular to the - //last line. Once it gets too big, the lines cannot be combined. - for (size_t i=0; i<Nx; i++) { - - thisx = *(double *)(xa->data + i*xa->strides[0]); - thisy = *(double *)(ya->data + i*ya->strides[0]); - - if (needNonlinear) - try { - // MGDTODO - // mpltransform->nonlinear_only_api(&thisx, &thisy); - } - catch (...) { - moveto = true; - continue; - } - if (MPL_isnan64(thisx) || MPL_isnan64(thisy)) { - moveto = true; - continue; - } - - //use agg's transformer? - xytrans.transform(&thisx, &thisy); - thisy = heightd - thisy; //flipy - - if (snapto) { - //disable subpixel rendering for horizontal or vertical lines of len=2 - //because it causes irregular line widths for grids and ticks - thisx = (int)thisx + 0.5; - thisy = (int)thisy + 0.5; - } - - //if we are starting a new path segment, move to the first point + init - if(moveto){ - path.move_to(thisx, thisy); - lastx = thisx; - lasty = thisy; - origdNorm2 = 0; //resets the orig-vector variables (see if-statement below) - moveto = false; - continue; - } - - //don't render line segments less that on pixel long! - if (fabs(thisx-lastx) < 1.0 && fabs(thisy-lasty) < 1.0 ){ - continue; //don't update lastx this time! - } - - //skip any lines that are outside the drawing area. Note: More lines - //could be clipped, but a more involved calculation would be needed - if( (thisx < 0 && lastx < 0 ) || - (thisx > width && lastx > width ) || - (thisy < 0 && lasty < 0 ) || - (thisy > height && lasty > height) ){ - lastx = thisx; - lasty = thisy; - clipped = true; - continue; - } - - //if we have no orig vector, set it to this vector and continue. - //this orig vector is the reference vector we will build up the line to - if(origdNorm2 == 0){ - //if we clipped after the moveto but before we got here, redo the moveto - if(clipped){ - path.move_to(lastx, lasty); - clipped = false; - } - - origdx = thisx - lastx; - origdy = thisy - lasty; - origdNorm2 = origdx*origdx + origdy*origdy; - - //set all the variables to reflect this new orig vecor - dnorm2Max = origdNorm2; - dnorm2Min = 0; - haveMin = false; - lastMax = true; - maxX = thisx; - maxY = thisy; - minX = lastx; - minY = lasty; - - lastWrittenx = lastx; - lastWritteny = lasty; - - //set the last point seen - lastx = thisx; - lasty = thisy; - continue; - } - - //if got to here, then we have an orig vector and we just got - //a vector in the sequence. - - //check that the perpendicular distance we have moved from the - //last written point compared to the line we are building is not too - //much. If o is the orig vector (we are building on), and v is the vector - //from the last written point to the current point, then the perpendicular - //vector is p = v - (o.v)o, and we normalize o (by dividing the - //second term by o.o). - - //get the v vector - totdx = thisx - lastWrittenx; - totdy = thisy - lastWritteny; - totdot = origdx*totdx + origdy*totdy; - - //get the para vector ( = (o.v)o/(o.o) ) - paradx = totdot*origdx/origdNorm2; - parady = totdot*origdy/origdNorm2; - paradNorm2 = paradx*paradx + parady*parady; - - //get the perp vector ( = v - para ) - perpdx = totdx - paradx; - perpdy = totdy - parady; - perpdNorm2 = perpdx*perpdx + perpdy*perpdy; - - //if the perp vector is less than some number of (squared) pixels in size, - //then merge the current vector - if(perpdNorm2 < 0.25 ){ - //check if the current vector is parallel or - //anti-parallel to the orig vector. If it is parallel, test - //if it is the longest of the vectors we are merging in that direction. - //If anti-p, test if it is the longest in the opposite direction (the - //min of our final line) - - lastMax = false; - if(totdot >= 0){ - if(paradNorm2 > dnorm2Max){ - lastMax = true; - dnorm2Max = paradNorm2; - maxX = lastWrittenx + paradx; - maxY = lastWritteny + parady; - } - } - else{ - - haveMin = true; - if(paradNorm2 > dnorm2Min){ - dnorm2Min = paradNorm2; - minX = lastWrittenx + paradx; - minY = lastWritteny + parady; - } - } - - lastx = thisx; - lasty = thisy; - continue; - } - - //if we get here, then this vector was not similar enough to the line - //we are building, so we need to draw that line and start the next one. - - //if the line needs to extend in the opposite direction from the direction - //we are drawing in, move back to we start drawing from back there. - if(haveMin){ - path.line_to(minX, minY); //would be move_to if not for artifacts - } - - path.line_to(maxX, maxY); - - //if we clipped some segments between this line and the next line - //we are starting, we also need to move to the last point. - if(clipped){ - path.move_to(lastx, lasty); - } - else if(!lastMax){ - //if the last line was not the longest line, then move back to the end - //point of the last line in the sequence. Only do this if not clipped, - //since in that case lastx,lasty is not part of the line just drawn. - path.line_to(lastx, lasty); //would be move_to if not for artifacts - } - - //std::cout << "draw lines (" << lastx << ", " << lasty << ")" << std::endl; - - //now reset all the variables to get ready for the next line - - origdx = thisx - lastx; - origdy = thisy - lasty; - origdNorm2 = origdx*origdx + origdy*origdy; - - dnorm2Max = origdNorm2; - dnorm2Min = 0; - haveMin = false; - lastMax = true; - maxX = thisx; - maxY = thisy; - minX = lastx; - minY = lasty; - - lastWrittenx = lastx; - lastWritteny = lasty; - - clipped = false; - - lastx = thisx; - lasty = thisy; - - counter++; - } - - //draw the last line, which is usually not drawn in the loop - if(origdNorm2 != 0){ - if(haveMin){ - path.line_to(minX, minY); //would be move_to if not for artifacts - } - - path.line_to(maxX, maxY); - } - - //std::cout << "drew " << counter+1 << " lines" << std::endl; - - Py_XDECREF(xa); - Py_XDECREF(ya); - - //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t; - //path_t transpath(path, xytrans); - _VERBOSE("RendererAgg::draw_lines rendering lines path"); - _render_lines_path(path, gc); - - _VERBOSE("RendererAgg::draw_lines DONE"); - return Py::Object(); - -} - -bool -RendererAgg::_process_alpha_mask(const GCAgg& gc) - //if gc has a clippath set, process the alpha mask and return True, - //else return False -{ - if (gc.clippath==NULL) { - return false; - } - if (0 &(gc.clippath==lastclippath)) { - //std::cout << "seen it" << std::endl; - return true; - } - rendererBaseAlphaMask->clear(agg::gray8(0, 0)); - gc.clippath->rewind(0); - theRasterizer->add_path(*(gc.clippath)); - rendererAlphaMask->color(agg::gray8(255,255)); - agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); - lastclippath = gc.clippath; - return true; -} - -template<class PathSource> -void -RendererAgg::_render_lines_path(PathSource &path, const GCAgg& gc) { - _VERBOSE("RendererAgg::_render_lines_path"); - typedef PathSource path_t; - //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t; - typedef agg::conv_stroke<path_t> stroke_t; - typedef agg::conv_dash<path_t> dash_t; - - bool isclippath(gc.clippath!=NULL); - - if (gc.dasha==NULL ) { //no dashes - stroke_t stroke(path); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - else { - dash_t dash(path); - - //todo: dash.dash_start(gc.dashOffset); - for (size_t i=0; i<gc.Ndash/2; i+=1) - dash.add_dash(gc.dasha[2*i], gc.dasha[2*i+1]); - - agg::conv_stroke<dash_t> stroke(dash); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.width(gc.linewidth); - theRasterizer->add_path(stroke); //boyle freeze is herre - } - - - if ( gc.isaa ) { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - } - else { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else{ - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } -} - -Py::Object RendererAgg::draw_markers(const Py::Tuple& args) { typedef agg::conv_transform<agg::path_storage> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; @@ -1284,10 +548,10 @@ if (!PathAgg::check(marker_path_obj)) throw Py::TypeError("Native path object is not of correct type"); PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr()); - agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]); + agg::trans_affine marker_trans = py_to_agg_transformation_matrix(args[2]); Py::Object vertices_obj = args[3]; Py::Object codes_obj = args[4]; - agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[5]); facepair_t face = _get_rgba_face(args[6], gc.alpha); // Deal with the difference in y-axis direction @@ -1337,7 +601,8 @@ unsigned strokeSize = scanlines.byte_size(); strokeCache = new agg::int8u[strokeSize]; // or any container scanlines.serialize(strokeCache); - + + // MGDTODO: Clean this up and support clippaths as well theRasterizer->reset_clipping(); if (gc.cliprect==NULL) { rendererBase->reset_clipping(true); @@ -1557,14 +822,20 @@ Py::Object RendererAgg::convert_to_native_path(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); - args.verify_length(2); + args.verify_length(1); - Py::Object vertices_obj = args[0]; - Py::Object codes_obj = args[1]; + Py::Object path = args[0]; + + return Py::asObject(new PathAgg(path)); +} + +PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) { + Py::Object vertices_obj = path_obj.getAttr("vertices"); + Py::Object codes_obj = path_obj.getAttr("codes"); + PyArrayObject* vertices = NULL; PyArrayObject* codes = NULL; - PathAgg* path = NULL; try { vertices = (PyArrayObject*)PyArray_ContiguousFromObject @@ -1576,8 +847,6 @@ if (!codes) throw Py::ValueError("Invalid codes array."); - path = new PathAgg(); - size_t next_vertex_stride = vertices->strides[0]; size_t next_axis_stride = vertices->strides[1]; size_t code_stride = codes->strides[0]; @@ -1593,31 +862,31 @@ switch (*(unsigned char*)(code_i)) { case MOVETO: GET_NEXT_VERTEX(x0, y0); - path->move_to(x0, y0); + move_to(x0, y0); _VERBOSE("MOVETO"); break; case LINETO: GET_NEXT_VERTEX(x0, y0); - path->line_to(x0, y0); + line_to(x0, y0); _VERBOSE("LINETO"); break; case CURVE3: GET_NEXT_VERTEX(x0, y0); GET_NEXT_VERTEX(x1, y1); - path->curve3(x0, y0, x1, y1); - path->curvy = true; + curve3(x0, y0, x1, y1); + curvy = true; _VERBOSE("CURVE3"); break; case CURVE4: GET_NEXT_VERTEX(x0, y0); GET_NEXT_VERTEX(x1, y1); GET_NEXT_VERTEX(x2, y2); - path->curve4(x0, y0, x1, y1, x2, y2); - path->curvy = true; + curve4(x0, y0, x1, y1, x2, y2); + curvy = true; _VERBOSE("CURVE4"); break; case CLOSEPOLY: - path->close_polygon(); + close_polygon(); _VERBOSE("CLOSEPOLY"); break; } @@ -1626,14 +895,11 @@ } catch(...) { Py_XDECREF(vertices); Py_XDECREF(codes); - delete path; throw; } Py_XDECREF(vertices); Py_XDECREF(codes); - - return Py::asObject(path); } Py::Object @@ -1643,7 +909,11 @@ typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; typedef agg::conv_stroke<dash_t> stroke_dash_t; - //draw_path(gc, rgbFace, path, transform) + typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; + typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; + typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; + typedef agg::renderer_scanline_bin_solid<amask_ren_type> amask_bin_renderer_type; + theRasterizer->reset_clipping(); _VERBOSE("RendererAgg::draw_path"); @@ -1654,52 +924,118 @@ if (!PathAgg::check(path_obj)) throw Py::TypeError("Native path object is not of correct type"); PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); - agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[2]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]); facepair_t face = _get_rgba_face(args[3], gc.alpha); trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - transformed_path_t tpath(*path, trans); - // MGDTODO: See if there is any advantage to only curving if necessary - curve_t curve(tpath); + transformed_path_t* tpath = NULL; + agg::path_storage new_path; - set_clipbox_rasterizer(gc.cliprect); - - if (face.first) { - rendererAA->color(face.second); - theRasterizer->add_path(curve); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + bool has_clippath = (gc.clippath != NULL); + + if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) { + rendererBaseAlphaMask->clear(agg::gray8(0, 0)); + gc.clippath->rewind(0); + transformed_path_t transformed_clippath(*(gc.clippath), trans); + theRasterizer->add_path(transformed_clippath); + rendererAlphaMask->color(agg::gray8(255, 255)); + agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); + lastclippath = gc.clippath; + lastclippath_transform = trans; } - if (gc.linewidth) { - if (gc.dasha == NULL) { - stroke_t stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } else { - dash_t dash(curve); - for (size_t i = 0; i < (gc.Ndash / 2); ++i) - dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]); - stroke_dash_t stroke(dash); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.width(gc.linewidth); - theRasterizer->add_path(stroke); //boyle freeze is herre + try { + // If this is a straight horizontal or vertical line, quantize to nearest + // pixels + if (path->total_vertices() == 2) { + double x0, y0, x1, y1; + path->vertex(0, &x0, &y0); + trans.transform(&x0, &y0); + path->vertex(1, &x1, &y1); + trans.transform(&x1, &y1); + if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { + new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); + new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); + tpath = new transformed_path_t(new_path, agg::trans_affine()); + } } + + if (!tpath) { + tpath = new transformed_path_t(*path, trans); + } + + // Benchmarking shows that there is no noticable slowdown to always + // treating paths as having curved segments. Doing so greatly + // simplifies the code + curve_t curve(*tpath); - if ( gc.isaa ) { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + set_clipbox_rasterizer(gc.cliprect); + + if (face.first) { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else{ + rendererAA->color(face.second); + theRasterizer->add_path(curve); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } } - else { - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + + if (gc.linewidth) { + if (gc.dasha == NULL) { + stroke_t stroke(curve); + stroke.width(gc.linewidth); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + theRasterizer->add_path(stroke); + } else { + dash_t dash(curve); + for (size_t i = 0; i < (gc.Ndash / 2); ++i) + dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]); + stroke_dash_t stroke(dash); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + stroke.width(gc.linewidth); + theRasterizer->add_path(stroke); + } + + if (gc.isaa) { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererAA->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } + } else { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_bin_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererBin->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + } + } } + } catch (...) { + delete tpath; + throw; } + delete tpath; + return Py::Object(); } @@ -2033,18 +1369,10 @@ behaviors().name("RendererAgg"); behaviors().doc("The agg backend extension module"); - add_varargs_method("draw_rectangle", &RendererAgg::draw_rectangle, - "draw_rectangle(gc, rgbFace, l, b, w, h)\n"); - add_varargs_method("draw_ellipse", &RendererAgg::draw_ellipse, - "draw_ellipse(gc, rgbFace, x, y, w, h)\n"); - add_varargs_method("draw_polygon", &RendererAgg::draw_polygon, - "draw_polygon(gc, rgbFace, points)\n"); add_varargs_method("draw_path",... [truncated message content]
Revision: 3854 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3854&view=rev Author: mdboom Date: 2007年09月17日 06:41:38 -0700 (2007年9月17日) Log Message: ----------- Transferring work-in-progress Modified Paths: -------------- branches/transforms/examples/shared_axis_demo.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/examples/shared_axis_demo.py =================================================================== --- branches/transforms/examples/shared_axis_demo.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/examples/shared_axis_demo.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -36,12 +36,12 @@ s2 = exp(-t) s3 = sin(4*pi*t) ax1 = subplot(311) -plot(t,s1) +plot(t,s1, "bH") setp( ax1.get_xticklabels(), fontsize=6) ## share x only ax2 = subplot(312, sharex=ax1) -plot(t, s2) +plot(t, s2, "b<") # make these tick labels invisible setp( ax2.get_xticklabels(), visible=False) Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -36,21 +36,25 @@ """ pass + def _get_cached_native_path(self, path): + native_path = self._native_paths.get(path) + if native_path is None: + import matplotlib.patches + print "CACHE MISS", path + native_path = self.convert_to_native_path(path) + self._native_paths[path] = native_path + return native_path + def draw_path(self, gc, path, transform, rgbFace = None): """ Handles the caching of the native path associated with the given path and calls the underlying backend's _draw_path to actually do the drawing. """ - native_path = self._native_paths.get(path) - if native_path is None: - import matplotlib.patches - print "CACHE MISS", path - native_path = self.convert_to_native_path(path) - self._native_paths[path] = native_path - self._draw_path(gc, native_path, transform, rgbFace) + native_path = self._get_cached_native_path(path) + self._draw_native_path(gc, native_path, transform, rgbFace) - def _draw_path(self, gc, native_path, transform, rgbFace): + def _draw_native_path(self, gc, native_path, transform, rgbFace): """ Draw the native path object with the given GraphicsContext and transform. The transform passed in will always be affine. @@ -107,9 +111,10 @@ return False def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - pass - - def _draw_markers(self, bgc, path, rgbFace, x, y, trans): + native_marker_path = self._get_cached_native_path(marker_path) + self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) + + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): """ This method is currently underscore hidden because the draw_markers method is being used as a sentinel for newstyle @@ -130,7 +135,7 @@ vec6 = transform.as_vec6_val() ...backend dependent affine... """ - pass + raise NotImplementedError def draw_line_collection(self, segments, transform, clipbox, colors, linewidths, linestyle, antialiaseds, Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -140,7 +140,7 @@ def convert_to_native_path(self, path): return self._renderer.convert_to_native_path(path.vertices, path.codes) - def _draw_path(self, gc, native_path, transform, rgbFace): + def _draw_native_path(self, gc, native_path, transform, rgbFace): return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace) def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): @@ -172,8 +172,12 @@ def draw_lines(self, gc, x, y, transform): return self._renderer.draw_lines(gc, x, y, transform.to_values()) - def draw_markers(self, gc, path, color, x, y, transform): - return self._renderer.draw_markers(gc, path, color, x, y, transform.to_values()) + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): + return self._renderer.draw_markers( + gc, + native_marker_path, marker_trans.to_values(), + path.vertices, path.codes, trans.to_values(), + rgbFace) def draw_polygon(self, *args): return self._renderer.draw_polygon(*args) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/lines.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -520,7 +520,7 @@ lineFunc(renderer, gc, self._path) # MGDTODO: Deal with markers - if self._marker is not None and False: + if self._marker is not None: gc = renderer.new_gc() self._set_gc_clip(gc) gc.set_foreground(self.get_markeredgecolor()) @@ -713,6 +713,9 @@ pass def _draw_steps(self, renderer, gc, xt, yt): + # MGDTODO: This is a quirky one. The step-plotting part + # should probably be moved to where the path is generated + # in recache, and then just drawn with _draw_solid siz=len(xt) if siz<2: return xt2=npy.ones((2*siz,), xt.dtype) @@ -727,323 +730,132 @@ renderer.draw_lines(gc, xt2, yt2) def _draw_solid(self, renderer, gc, path): - # if len(xt)<2: return gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) - def _draw_dashed(self, renderer, gc, xt, yt): - if len(xt)<2: return + def _draw_dashed(self, renderer, gc, path): gc.set_linestyle('dashed') if self._dashSeq is not None: gc.set_dashes(0, self._dashSeq) - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_dash_dot(self, renderer, gc, xt, yt): - if len(xt)<2: return + def _draw_dash_dot(self, renderer, gc, path): gc.set_linestyle('dashdot') - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_dotted(self, renderer, gc, xt, yt): - - if len(xt)<2: return + + def _draw_dotted(self, renderer, gc, path): gc.set_linestyle('dotted') - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_point(self, renderer, gc, xt, yt): + + def _draw_point(self, renderer, gc, path): + self._draw_circle(renderer, gc, path, point = True) - r = 0.5 * renderer.points_to_pixels(self._markersize) - r *= self._point_size_reduction - gc.set_linewidth(0) - if r <= 0.5: - self._draw_pixel(renderer, gc, xt, yt) - elif r <= 2: - self._draw_hexagon1(renderer, gc, xt, yt, point=True) - else: - self._draw_circle(renderer, gc, xt, yt, point=True) - - def _draw_pixel(self, renderer, gc, xt, yt): - if self._newstyle: - rgbFace = self._get_rgb_face() - path = agg.path_storage() - path.move_to(-0.5, -0.5) - path.line_to(-0.5, 0.5) - path.line_to(0.5, 0.5) - path.line_to(0.5, -0.5) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_point(gc, x, y) - - - def _draw_circle(self, renderer, gc, xt, yt, point=False): - + def _draw_pixel(self, renderer, gc, path): + rgbFace = self._get_rgb_face() + transform = Affine2D().translate(-0.5, -0.5) + renderer.draw_markers(gc, Path.unit_rectangle, transform, + path, self.get_transform(), rgbFace) + + + def _draw_circle(self, renderer, gc, path, point=False): w = renderer.points_to_pixels(self._markersize) if point: w *= self._point_size_reduction + w *= 0.5 - rgbFace = self._get_rgb_face() + transform = Affine2D().scale(w, w) + renderer.draw_markers( + gc, Path.unit_circle(), transform, path, self.get_transform(), + rgbFace) - if self._newstyle: - N = 50.0 - r = w/2. - rads = (2*math.pi/N)*npy.arange(N) - xs = r*npy.cos(rads) - ys = r*npy.sin(rads) - # todo: use curve3! - path = agg.path_storage() - path.move_to(xs[0], ys[0]) - for x, y in zip(xs[1:], ys[1:]): - path.line_to(x, y) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt,yt): - renderer.draw_arc(gc, rgbFace, - x, y, w, w, 0.0, 360.0, 0.0) - - - - def _draw_triangle_up(self, renderer, gc, xt, yt): - - + _triangle_path = Path([[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0]]) + def _draw_triangle_up(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offset, -offset) - path.line_to(offset, -offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offset, y-offset), - (x+offset, y-offset) ) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_triangle_down(self, renderer, gc, xt, yt): + def _draw_triangle_down(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, -offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - - path = agg.path_storage() - path.move_to(-offset, offset) - path.line_to(offset, offset) - path.line_to(0, -offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x-offset, y+offset), - (x+offset, y+offset), - (x, y-offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_triangle_left(self, renderer, gc, xt, yt): + + def _draw_triangle_left(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset).rotate_deg(90) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to(offset, -offset) - path.line_to(offset, offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x-offset, y), - (x+offset, y-offset), - (x+offset, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - - def _draw_triangle_right(self, renderer, gc, xt, yt): + def _draw_triangle_right(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset).rotate_deg(-90) rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(-offset, -offset) - path.line_to(-offset, offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x-offset, y-offset), - (x-offset, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - - def _draw_square(self, renderer, gc, xt, yt): + def _draw_square(self, renderer, gc, path): side = renderer.points_to_pixels(self._markersize) - offset = side*0.5 + transform = Affine2D().translate(-0.5, -0.5).scale(side) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - - path = agg.path_storage() - path.move_to(-offset, -offset) - path.line_to(-offset, offset) - path.line_to(offset, offset) - path.line_to(offset, -offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - - for (x,y) in zip(xt, yt): - renderer.draw_rectangle( - gc, rgbFace, - x-offset, y-offset, side, side) - - def _draw_diamond(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) + + def _draw_diamond(self, renderer, gc, path): + side = renderer.points_to_pixels(self._markersize) + transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(side) rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(0, -offset) - path.line_to(-offset, 0) - path.line_to(0, offset) - path.end_poly() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - - - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x, y-offset), - (x-offset, y), - (x, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_thin_diamond(self, renderer, gc, xt, yt): - offset = 0.7*renderer.points_to_pixels(self._markersize) - xoffset = 0.6*offset + + def _draw_thin_diamond(self, renderer, gc, path): + offset = renderer.points_to_pixels(self._markersize) + transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(xoffset, 0) - path.line_to(0, -offset) - path.line_to(-xoffset, 0) - path.line_to(0, offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+xoffset, y), - (x, y-offset), - (x-xoffset, y), - (x, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) + + def _draw_pentagon(self, renderer, gc, path): + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(5), transform, + path, self.get_transform(), rgbFace) - def _draw_pentagon(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) - offsetX1 = offset*0.95 - offsetY1 = offset*0.31 - offsetX2 = offset*0.59 - offsetY2 = offset*0.81 - rgbFace = self._get_rgb_face() + + def _draw_hexagon1(self, renderer, gc, path, point=False): + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offsetX2, -offsetY2) - path.line_to(+offsetX2, -offsetY2) - path.line_to(+offsetX1, offsetY1) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offsetX1, y+offsetY1), - (x-offsetX2, y-offsetY2), - (x+offsetX2, y-offsetY2), - (x+offsetX1, y+offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_hexagon1(self, renderer, gc, xt, yt, point=False): - offset = 0.6*renderer.points_to_pixels(self._markersize) - if point: - offset *= self._point_size_reduction - offsetX1 = offset*0.87 - offsetY1 = offset*0.5 - rgbFace = self._get_rgb_face() - - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offsetX1, -offsetY1) - path.line_to(0, -offset) - path.line_to(offsetX1, -offsetY1) - path.line_to(offsetX1, offsetY1) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offsetX1, y+offsetY1), - (x-offsetX1, y-offsetY1), - (x, y-offset), - (x+offsetX1, y-offsetY1), - (x+offsetX1, y+offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - + def _draw_hexagon2(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) - offsetX1 = offset*0.5 - offsetY1 = offset*0.87 - rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(offsetX1, offsetY1) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offset, 0) - path.line_to(-offsetX1, -offsetY1) - path.line_to(offsetX1, -offsetY1) - path.end_poly() + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(30) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, + path, self.get_transform(), rgbFace) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x+offsetX1, y+offsetY1), - (x-offsetX1, y+offsetY1), - (x-offset, y), - (x-offsetX1, y-offsetY1), - (x+offsetX1, y-offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - + def _draw_vline(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) if self._newstyle: @@ -1055,6 +867,7 @@ for (x,y) in zip(xt, yt): renderer.draw_line(gc, x, y-offset, x, y+offset) + def _draw_hline(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) if self._newstyle: @@ -1066,46 +879,31 @@ for (x,y) in zip(xt, yt): renderer.draw_line(gc, x-offset, y, x+offset, y) - def _draw_tickleft(self, renderer, gc, xt, yt): + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) + def _draw_tickleft(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0.5) - path.line_to(0, 0.5) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x, y) + marker_transform = Affine2D().scale(offset, 1.0) + renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, + path, self.get_transform()) - def _draw_tickright(self, renderer, gc, xt, yt): - + def _draw_tickright(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0.5) - path.line_to(offset, 0.5) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x+offset, y) + marker_transform = Affine2D().scale(-offset, 1.0) + renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, + path, self.get_transform()) - _tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) - def _draw_tickup(self, renderer, gc, xt, yt): + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) + def _draw_tickup(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, offset) - renderer.draw_markers(gc, self._tickup_path, marker_transform, - self._path, self.get_transform()) + renderer.draw_markers(gc, self._tickvert_path, marker_transform, + path, self.get_transform()) - def _draw_tickdown(self, renderer, gc, xt, yt): + def _draw_tickdown(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-0.5, -offset) - path.line_to(-0.5, 0) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y-offset, x, y) + marker_transform = Affine2D().scale(1.0, -offset) + renderer.draw_markers(gc, self._tickvert_path, marker_transform, + path, self.get_transform()) def _draw_plus(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/patches.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -288,7 +288,6 @@ self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - def _update(self): self.update_from(self.patch) if self.props is not None: @@ -316,8 +315,7 @@ """ - _path = Path( - [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + _path = Path.unit_rectangle() def __str__(self): return str(self.__class__).split('.')[-1] \ @@ -424,8 +422,6 @@ """ A regular polygon patch. """ - _polygon_cache = {} - def __str__(self): return "Poly%d(%g,%g)"%(self.numVertices,self.xy[0],self.xy[1]) @@ -442,14 +438,7 @@ """ Patch.__init__(self, **kwargs) - path = self._polygon_cache[numVertices] - if path is None: - theta = 2*npy.pi/numVertices * npy.arange(numVertices) - verts = npy.hstack((npy.cos(theta), npy.sin(theta))) - path = Path(verts) - self._polygon_cache[numVertices] = path - - self._path = path + self._path = Path.unit_regular_polygon(numVertices) self._poly_transform = transforms.Affine2D() \ .scale(radius) \ .rotate(orientation) \ Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/path.py 2007年09月17日 13:41:38 UTC (rev 3854) @@ -1,21 +1,25 @@ import numpy as npy -class Path: +VALIDATE_PATHS = True + +class Path(object): # Path codes STOP = 0 MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices + CLOSEPOLY = 5 ### # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, # so if they don't, we probably shouldn't - CURVEN = 5 - CATROM = 6 - UBSPLINE = 7 + CURVEN = 6 + CATROM = 7 + UBSPLINE = 8 #### - CLOSEPOLY = 0x0F # 0 vertices + NUM_VERTICES = [0, 1, 1, 2, 3, 0] + code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): @@ -38,9 +42,14 @@ self._codes = codes assert self._codes.ndim == 1 - # MGDTODO: Maybe we should add some quick-and-dirty check that - # the number of vertices is correct for the code array + if VALIDATE_PATHS: + i = 0 + NUM_VERTICES = self.NUM_VERTICES + for code in codes: + i += NUM_VERTICES[code] + assert i == len(self.vertices) + def _get_codes(self): return self._codes codes = property(_get_codes) @@ -48,3 +57,74 @@ def _get_vertices(self): return self._vertices vertices = property(_get_vertices) + + def iter_endpoints(self): + i = 0 + NUM_VERTICES = self.NUM_VERTICES + vertices = self.vertices + for code in self.codes: + num_vertices = NUM_VERTICES[code] + if num_vertices >= 1: + i += num_vertices - 1 + yield vertices[i] + i += 1 + + _unit_rectangle = None + #@classmethod + def unit_rectangle(cls): + if cls._unit_rectangle is None: + cls._unit_rectangle = \ + Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + return cls._unit_rectangle + unit_rectangle = classmethod(unit_rectangle) + + _unit_regular_polygons = {} + #@classmethod + def unit_regular_polygon(cls, numVertices): + path = cls._unit_regular_polygons.get(numVertices) + if path is None: + theta = 2*npy.pi/numVertices * npy.arange(numVertices) + # This is to make sure the polygon always "points-up" + theta += npy.pi / 2.0 + verts = npy.vstack((npy.cos(theta), npy.sin(theta))).transpose() + path = Path(verts) + cls._unit_regular_polygons[numVertices] = path + return path + unit_regular_polygon = classmethod(unit_regular_polygon) + + _unit_circle = None + #@classmethod + def unit_circle(cls): + # MGDTODO: Optimize? + if cls._unit_circle is None: + offset = 4.0 * (npy.sqrt(2) - 1) / 3.0 + vertices = npy.array( + [[-1.0, 0.0], + + [-1.0, offset], + [-offset, 1.0], + [0.0, 1.0], + + [offset, 1.0], + [1.0, offset], + [1.0, 0.0], + + [1.0, -offset], + [offset, -1.0], + [0.0, -1.0], + + [-offset, -1.0], + [-1.0, -offset], + [-1.0, 0.0]], + npy.float_) + codes = npy.array( + [cls.MOVETO, + cls.CURVE4, + cls.CURVE4, + cls.CURVE4, + cls.CURVE4, + cls.CLOSEPOLY], + cls.code_type) + cls._unit_circle = Path(vertices, codes) + return cls._unit_circle + unit_circle = classmethod(unit_circle) Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/src/_backend_agg.cpp 2007年09月17日 13:41:38 UTC (rev 3854) @@ -64,6 +64,20 @@ Py::Float(seq[5])); } +// MGDTODO: Implement this as a nice iterator +inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, + double& x, double& y, + size_t next_vertex_stride, + size_t next_axis_stride) { + if (vertex_i + next_axis_stride >= vertex_end) + throw Py::ValueError("Error parsing path. Read past end of vertices"); + x = *(double*)vertex_i; + y = *(double*)(vertex_i + next_axis_stride); + vertex_i += next_vertex_stride; +} + +#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), @@ -1255,146 +1269,134 @@ Py::Object RendererAgg::draw_markers(const Py::Tuple& args) { + typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_curve<transformed_path_t> curve_t; + typedef agg::conv_stroke<curve_t> stroke_t; + typedef agg::conv_dash<curve_t> dash_t; + typedef agg::conv_stroke<dash_t> stroke_dash_t; + theRasterizer->reset_clipping(); - _VERBOSE("RendererAgg::_draw_markers_cache"); - args.verify_length(6); + args.verify_length(7); - _VERBOSE("RendererAgg::_draw_markers_cache setting gc"); GCAgg gc = GCAgg(args[0], dpi); + Py::Object marker_path_obj = args[1]; + if (!PathAgg::check(marker_path_obj)) + throw Py::TypeError("Native path object is not of correct type"); + PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr()); + agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]); + Py::Object vertices_obj = args[3]; + Py::Object codes_obj = args[4]; + agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]); + facepair_t face = _get_rgba_face(args[6], gc.alpha); + + // Deal with the difference in y-axis direction + marker_trans *= agg::trans_affine_scaling(1.0, -1.0); + trans *= agg::trans_affine_scaling(1.0, -1.0); + trans *= agg::trans_affine_translation(0.0, (double)height); + marker_path->rewind(0); + transformed_path_t marker_path_transformed(*marker_path, marker_trans); + curve_t marker_path_curve(marker_path_transformed); - agg::path_storage *ppath; - - swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *"); - assert(descr); - if (SWIG_ConvertPtr(args[1].ptr(),(void **)(&ppath), descr, 0) == -1) { - throw Py::TypeError("Could not convert path_storage"); - } - facepair_t face = _get_rgba_face(args[2], gc.alpha); - - Py::Object xo = args[3]; - Py::Object yo = args[4]; - - PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1); - - if (xa==NULL) - throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array"); - - - PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1); - - if (ya==NULL) - throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array"); - - agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[5]); - - size_t Nx = xa->dimensions[0]; - size_t Ny = ya->dimensions[0]; - - if (Nx!=Ny) - throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str()); - - - double heightd = double(height); - - - ppath->rewind(0); - ppath->flip_y(0,0); - typedef agg::conv_curve<agg::path_storage> curve_t; - curve_t curve(*ppath); - //maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; theRasterizer->reset(); agg::int8u* fillCache = NULL; - unsigned fillSize = 0; - if (face.first) { - theRasterizer->add_path(curve); + agg::int8u* strokeCache = NULL; + PyArrayObject* vertices = NULL; + PyArrayObject* codes = NULL; + + try { + vertices = (PyArrayObject*)PyArray_ContiguousFromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) + throw Py::ValueError("Invalid vertices array."); + codes = (PyArrayObject*)PyArray_ContiguousFromObject + (codes_obj.ptr(), PyArray_UINT8, 1, 1); + if (!codes) + throw Py::ValueError("Invalid codes array."); + + unsigned fillSize = 0; + if (face.first) { + theRasterizer->add_path(marker_path_curve); + agg::render_scanlines(*theRasterizer, *slineP8, scanlines); + fillSize = scanlines.byte_size(); + fillCache = new agg::int8u[fillSize]; // or any container + scanlines.serialize(fillCache); + } + + stroke_t stroke(marker_path_curve); + stroke.width(gc.linewidth); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + theRasterizer->reset(); + theRasterizer->add_path(stroke); agg::render_scanlines(*theRasterizer, *slineP8, scanlines); - fillSize = scanlines.byte_size(); - fillCache = new agg::int8u[fillSize]; // or any container - scanlines.serialize(fillCache); - } + unsigned strokeSize = scanlines.byte_size(); + strokeCache = new agg::int8u[strokeSize]; // or any container + scanlines.serialize(strokeCache); - agg::conv_stroke<curve_t> stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->reset(); - theRasterizer->add_path(stroke); - agg::render_scanlines(*theRasterizer, *slineP8, scanlines); - unsigned strokeSize = scanlines.byte_size(); - agg::int8u* strokeCache = new agg::int8u[strokeSize]; // or any container - scanlines.serialize(strokeCache); - - theRasterizer->reset_clipping(); - - - if (gc.cliprect==NULL) { - rendererBase->reset_clipping(true); - } - else { - int l = (int)(gc.cliprect[0]) ; - int b = (int)(gc.cliprect[1]) ; - int w = (int)(gc.cliprect[2]) ; - int h = (int)(gc.cliprect[3]) ; - rendererBase->clip_box(l, height-(b+h),l+w, height-b); - } - - - double thisx, thisy; - for (size_t i=0; i<Nx; i++) { - thisx = *(double *)(xa->data + i*xa->strides[0]); - thisy = *(double *)(ya->data + i*ya->strides[0]); - - // MGDTODO -// if (mpltransform->need_nonlinear_api()) -// try { -// mpltransform->nonlinear_only_api(&thisx, &thisy); -// } -// catch(...) { -// continue; -// } + theRasterizer->reset_clipping(); + if (gc.cliprect==NULL) { + rendererBase->reset_clipping(true); + } + else { + int l = (int)(gc.cliprect[0]) ; + int b = (int)(gc.cliprect[1]) ; + int w = (int)(gc.cliprect[2]) ; + int h = (int)(gc.cliprect[3]) ; + rendererBase->clip_box(l, height-(b+h),l+w, height-b); + } - xytrans.transform(&thisx, &thisy); - - thisy = heightd - thisy; //flipy - - thisx = (int)thisx + 0.5; - thisy = (int)thisy + 0.5; - if (thisx<0) continue; - if (thisy<0) continue; - if (thisx>width) continue; - if (thisy>height) continue; - + size_t next_vertex_stride = vertices->strides[0]; + size_t next_axis_stride = vertices->strides[1]; + size_t code_stride = codes->strides[0]; + + const char* vertex_i = vertices->data; + const char* code_i = codes->data; + const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]); + + size_t N = codes->dimensions[0]; + double x, y; + agg::serialized_scanlines_adaptor_aa8 sa; agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; - - if (face.first) { - //render the fill - sa.init(fillCache, fillSize, thisx, thisy); - rendererAA->color(face.second); - agg::render_scanlines(sa, sl, *rendererAA); + + for (size_t i=0; i < N; i++) { + size_t num_vertices = NUM_VERTICES[(int)(*code_i)]; + if (num_vertices) { + for (size_t j=0; j<num_vertices; ++j) + GET_NEXT_VERTEX(x, y); + trans.transform(&x, &y); + + if (face.first) { + //render the fill + sa.init(fillCache, fillSize, x, y); + rendererAA->color(face.second); + agg::render_scanlines(sa, sl, *rendererAA); + } + + //render the stroke + sa.init(strokeCache, strokeSize, x, y); + rendererAA->color(gc.color); + agg::render_scanlines(sa, sl, *rendererAA); + } + code_i += code_stride; } - - //render the stroke - sa.init(strokeCache, strokeSize, thisx, thisy); - rendererAA->color(gc.color); - agg::render_scanlines(sa, sl, *rendererAA); - - } //for each marker + } catch(...) { + Py_XDECREF(vertices); + Py_XDECREF(codes); + delete[] fillCache; + delete[] strokeCache; + } - Py_XDECREF(xa); - Py_XDECREF(ya); - - if (face.first) - delete [] fillCache; + Py_XDECREF(vertices); + Py_XDECREF(codes); + delete [] fillCache; delete [] strokeCache; - //jdh - _VERBOSE("RendererAgg::_draw_markers_cache done"); return Py::Object(); } @@ -1552,21 +1554,6 @@ } -inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, - double& x, double& y, - size_t next_vertex_stride, - size_t next_axis_stride) { - if (vertex_i + next_axis_stride >= vertex_end) - throw Py::ValueError("Error parsing path. Read past end of vertices"); - x = *(double*)vertex_i; - y = *(double*)(vertex_i + next_axis_stride); - vertex_i += next_vertex_stride; -} - -#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) - - - Py::Object RendererAgg::convert_to_native_path(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); @@ -2059,7 +2046,7 @@ add_varargs_method("draw_lines", &RendererAgg::draw_lines, "draw_lines(gc, x, y,)\n"); add_varargs_method("draw_markers", &RendererAgg::draw_markers, - "draw_markers(gc, path, x, y)\n"); + "draw_markers(gc, marker_path, marker_trans, vertices, codes, rgbFace)\n"); add_varargs_method("draw_text_image", &RendererAgg::draw_text_image, "draw_text_image(font_image, x, y, r, g, b, a)\n"); add_varargs_method("draw_image", &RendererAgg::draw_image, Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007年09月15日 04:01:56 UTC (rev 3853) +++ branches/transforms/src/_backend_agg.h 2007年09月17日 13:41:38 UTC (rev 3854) @@ -45,8 +45,10 @@ #define LINETO 2 #define CURVE3 3 #define CURVE4 4 -#define CLOSEPOLY 0x0F +#define CLOSEPOLY 5 +const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 }; + typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.