From: <jd...@us...> - 2008-12-18 19:40:36
|
Revision: 6677 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6677&view=rev Author: jdh2358 Date: 2008-12-18 19:40:26 +0000 (Thu, 18 Dec 2008) Log Message: ----------- Merged revisions 6675-6676 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6675 | jdh2358 | 2008-12-18 11:19:26 -0800 (Thu, 18 Dec 2008) | 1 line fixed a small_docs bug when no args present ........ r6676 | jdh2358 | 2008-12-18 11:28:46 -0800 (Thu, 18 Dec 2008) | 1 line pushed out new 98.5.2 tarball with smalldocs fix ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/make.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6673 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6676 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-18 19:28:46 UTC (rev 6676) +++ trunk/matplotlib/CHANGELOG 2008-12-18 19:40:26 UTC (rev 6677) @@ -1,7 +1,8 @@ 2008-12-18 add new arrow style, a line + filled triangles. -JJL ================================================================== -2008-12-18 Released 0.98.5.2 from v0_98_5_maint at r6667 +2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6675 + Released 0.98.5.2 from v0_98_5_maint at r6667 2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH Modified: trunk/matplotlib/doc/make.py =================================================================== --- trunk/matplotlib/doc/make.py 2008-12-18 19:28:46 UTC (rev 6676) +++ trunk/matplotlib/doc/make.py 2008-12-18 19:40:26 UTC (rev 6677) @@ -105,4 +105,5 @@ arg, funcd.keys())) func() else: + small_docs = False all() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-19 12:40:16
|
Revision: 6682 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6682&view=rev Author: jdh2358 Date: 2008-12-19 12:40:09 +0000 (Fri, 19 Dec 2008) Log Message: ----------- Merged revisions 6679-6680 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6679 | jdh2358 | 2008-12-19 06:29:49 -0600 (Fri, 19 Dec 2008) | 1 line added macosx.m to manifest ........ r6680 | jdh2358 | 2008-12-19 06:30:33 -0600 (Fri, 19 Dec 2008) | 1 line added macosx.m to manifest; released 98.5.2 tarball ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/MANIFEST.in Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6676 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6681 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-19 12:38:33 UTC (rev 6681) +++ trunk/matplotlib/CHANGELOG 2008-12-19 12:40:09 UTC (rev 6682) @@ -1,7 +1,7 @@ 2008-12-18 add new arrow style, a line + filled triangles. -JJL ================================================================== -2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6675 +2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6679 Released 0.98.5.2 from v0_98_5_maint at r6667 2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH Modified: trunk/matplotlib/MANIFEST.in =================================================================== --- trunk/matplotlib/MANIFEST.in 2008-12-19 12:38:33 UTC (rev 6681) +++ trunk/matplotlib/MANIFEST.in 2008-12-19 12:40:09 UTC (rev 6682) @@ -16,7 +16,7 @@ recursive-include license LICENSE* recursive-include examples * recursive-include doc * -recursive-include src *.cpp *.c *.h +recursive-include src *.cpp *.c *.h *.m recursive-include CXX *.cxx *.hxx *.c *.h recursive-include agg24 * recursive-include lib * This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-19 22:03:12
|
Revision: 6686 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6686&view=rev Author: leejjoon Date: 2008-12-19 21:41:11 +0000 (Fri, 19 Dec 2008) Log Message: ----------- Merged revisions 6685 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6685 | leejjoon | 2008-12-19 16:24:32 -0500 (Fri, 19 Dec 2008) | 1 line update legend-related document ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/api/api_changes.rst trunk/matplotlib/lib/matplotlib/__init__.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/legend.py trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template trunk/matplotlib/lib/matplotlib/rcsetup.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6681 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6685 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/CHANGELOG 2008-12-19 21:41:11 UTC (rev 6686) @@ -1,3 +1,7 @@ +2008-12-19 Update Axes.legend documnetation. /api/api_changes.rst is also + updated to describe chages in keyword parameters. + Issue a warning if old keyword parameters are used. - JJL + 2008-12-18 add new arrow style, a line + filled triangles. -JJL ================================================================== Modified: trunk/matplotlib/doc/api/api_changes.rst =================================================================== --- trunk/matplotlib/doc/api/api_changes.rst 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/doc/api/api_changes.rst 2008-12-19 21:41:11 UTC (rev 6686) @@ -15,6 +15,22 @@ Changes for 0.98.x ================== +* Following keyword parameters for :class:`matplotlib.label.Label` are now + deprecated and new set of parameters are introduced. The new parameters + are given as a fraction of the font-size. Also, *scatteryoffsets*, + *fancybox* and *columnspacing* are added as keyword parameters. + + ================ ================ + Deprecated New + ================ ================ + pad borderpad + labelsep labelspacing + handlelen handlelength + handlestextsep handletextpad + axespad borderaxespad + ================ ================ + + * Removed the configobj and experiemtnal traits rc support * Modified :func:`matplotlib.mlab.psd`, :func:`matplotlib.mlab.csd`, Modified: trunk/matplotlib/lib/matplotlib/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/__init__.py 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/lib/matplotlib/__init__.py 2008-12-19 21:41:11 UTC (rev 6686) @@ -582,7 +582,15 @@ 'tick.size' : 'tick.major.size', } +_deprecated_ignore_map = { + 'legend.pad' : 'legend.borderpad', + 'legend.labelsep' : 'legend.labelspacing', + 'legend.handlelen' : 'legend.handlelength', + 'legend.handletextsep' : 'legend.handletextpad', + 'legend.axespad' : 'legend.borderaxespad', + } + class RcParams(dict): """ @@ -602,6 +610,10 @@ warnings.warn('%s is deprecated in matplotlibrc. Use %s \ instead.'% (key, alt)) key = alt + elif key in _deprecated_ignore_map: + alt = _deprecated_ignore_map[key] + warnings.warn('%s is deprecated. Use %s instead.'% (key, alt)) + return cval = self.validate[key](val) dict.__setitem__(self, key, cval) except KeyError: @@ -665,6 +677,9 @@ except Exception, msg: warnings.warn('Bad val "%s" on line #%d\n\t"%s"\n\tin file \ "%s"\n\t%s' % (val, cnt, line, fname, msg)) + elif key in _deprecated_ignore_map: + warnings.warn('%s is deprecated. Update your matplotlibrc to use %s instead.'% (key, _deprecated_ignore_map[key])) + else: print >> sys.stderr, """ Bad key "%s" on line %d in Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-12-19 21:41:11 UTC (rev 6686) @@ -3740,10 +3740,6 @@ A :class:`matplotlib.font_manager.FontProperties` instance, or *None* to use rc settings. - *pad*: [ None | scalar ] - The fractional whitespace inside the legend border, between 0 and 1. - If *None*, use rc settings. - *markerscale*: [ None | scalar ] The relative size of legend markers vs. original. If *None*, use rc settings. @@ -3751,21 +3747,21 @@ *shadow*: [ None | False | True ] If *True*, draw a shadow behind legend. If *None*, use rc settings. - *labelsep*: [ None | scalar ] - The vertical space between the legend entries. If *None*, use rc - settings. + Padding and spacing between various elements use following keywords + parameters. The dimensions of these values are given as a fraction + of the fontsize. Values from rcParams will be used if None. - *handlelen*: [ None | scalar ] - The length of the legend lines. If *None*, use rc settings. + ================ ================================================================== + Keyword Description + ================ ================================================================== + borderpad the fractional whitespace inside the legend border + labelspacing the vertical space between the legend entries + handlelength the length of the legend handles + handletextpad the pad between the legend handle and text + borderaxespad the pad between the axes and legend border + columnspacing the spacing between columns + ================ ================================================================== - *handletextsep*: [ None | scalar ] - The space between the legend line and legend text. If *None*, use rc - settings. - - *axespad*: [ None | scalar ] - The border between the axes and legend edge. If *None*, use rc - settings. - **Example:** .. plot:: mpl_examples/api/legend_demo.py Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-19 21:41:11 UTC (rev 6686) @@ -98,7 +98,7 @@ handletextsep = None, # deprecated; use handletextpad axespad = None, # deprecated; use borderaxespad - # spacing & pad defined as a fractionof the font-size + # spacing & pad defined as a fraction of the font-size borderpad = None, # the whitespace inside the legend border labelspacing=None, #the vertical space between the legend entries handlelength=None, # the length of the legend handles Modified: trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template =================================================================== --- trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template 2008-12-19 21:41:11 UTC (rev 6686) @@ -233,19 +233,11 @@ origin = 'upper' [legend] - # a float - axespad = 0.02 # a float or 'xx-small' or 'x-small' or 'small' or 'medium' or 'large' or # 'x-large' or 'xx-large' fontsize = 'medium' - # a float - handlelen = 0.050000000000000003 - # a float - handletextsep = 0.02 # a boolean isaxes = True - # a float - labelsep = 0.01 # 'best' or 'upper right' or 'upper left' or 'lower left' or 'lower right' # or 'right' or 'center left' or 'center right' or 'lower center' or # 'upper center' or 'center' @@ -254,11 +246,22 @@ markerscale = 1.0 # an integer numpoints = 3 - # a float - pad = 0.20000000000000001 # a boolean shadow = False + # float + borderpad = 0.4 + # float + labelspacing = 0.5 + # float + handlelength = 2. + # float + handletextpad = 0.8 + # float + borderaxespad = 0.5 + # float + columnspacing = 2. + [lines] # a boolean antialiased = True Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-19 21:24:32 UTC (rev 6685) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-19 21:41:11 UTC (rev 6686) @@ -432,18 +432,12 @@ 'legend.isaxes' : [True,validate_bool], # this option is internally ignored - it never served any useful purpose 'legend.numpoints' : [2, validate_int], # the number of points in the legend line 'legend.fontsize' : ['large', validate_fontsize], - 'legend.pad' : [0, validate_float], # was 0.2, deprecated; the fractional whitespace inside the legend border - 'legend.borderpad' : [0.4, validate_float], # units are fontsize 'legend.markerscale' : [1.0, validate_float], # the relative size of legend markers vs. original - - # the following dimensions are in axes coords - 'legend.labelsep' : [0.010, validate_float], # the vertical space between the legend entries - 'legend.handlelen' : [0.05, validate_float], # the length of the legend lines - 'legend.handletextsep' : [0.02, validate_float], # the space between the legend line and legend text - 'legend.axespad' : [0.02, validate_float], # the border between the axes and legend edge 'legend.shadow' : [False, validate_bool], + # the following dimensions are in fraction of the font size + 'legend.borderpad' : [0.4, validate_float], # units are fontsize 'legend.labelspacing' : [0.5, validate_float], # the vertical space between the legend entries 'legend.handlelength' : [2., validate_float], # the length of the legend lines 'legend.handletextpad' : [.8, validate_float], # the space between the legend line and legend text @@ -453,11 +447,6 @@ 'legend.markerscale' : [1.0, validate_float], # the relative size of legend markers vs. original - # the following dimensions are in axes coords - 'legend.labelsep' : [0.010, validate_float], # the vertical space between the legend entries - 'legend.handlelen' : [0.05, validate_float], # the length of the legend lines - 'legend.handletextsep' : [0.02, validate_float], # the space between the legend line and legend text - 'legend.axespad' : [0.5, validate_float], # the border between the axes and legend edge 'legend.shadow' : [False, validate_bool], This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-19 22:48:15
|
Revision: 6687 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6687&view=rev Author: leejjoon Date: 2008-12-19 22:48:11 +0000 (Fri, 19 Dec 2008) Log Message: ----------- axes_locator in the Axes class Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/axes_divider.py trunk/matplotlib/examples/pylab_examples/axes_grid.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-19 21:41:11 UTC (rev 6686) +++ trunk/matplotlib/CHANGELOG 2008-12-19 22:48:11 UTC (rev 6687) @@ -1,3 +1,6 @@ +2008-12-19 Add axes_locator attribute in Axes. Two examples are added. + - JJL + 2008-12-19 Update Axes.legend documnetation. /api/api_changes.rst is also updated to describe chages in keyword parameters. Issue a warning if old keyword parameters are used. - JJL Added: trunk/matplotlib/examples/pylab_examples/axes_divider.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/axes_divider.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/axes_divider.py 2008-12-19 22:48:11 UTC (rev 6687) @@ -0,0 +1,638 @@ + +import matplotlib.axes as maxes +import matplotlib.transforms as mtransforms + +import matplotlib.cbook as cbook + +import new + + +class Size(object): + + @classmethod + def from_any(self, size, fraction_ref=None): + if cbook.is_numlike(size): + return Size.Fixed(size) + elif cbook.is_string_like(size): + if size[-1] == "%": + return Size.Fraction(fraction_ref, float(size[:-1])/100.) + + raise ValueError("") + + + + class _Base(object): + pass + + class Fixed(_Base): + def __init__(self, fixed_size): + self._fixed_size = fixed_size + + def get_size(self, renderer): + rel_size = 0. + abs_size = self._fixed_size + return rel_size, abs_size + + class Scalable(_Base): + def __init__(self, scalable_size): + self._scalable_size = scalable_size + + def get_size(self, renderer): + rel_size = self._scalable_size + abs_size = 0. + return rel_size, abs_size + + + class AxesX(_Base): + def __init__(self, axes, aspect=1.): + self._axes = axes + self._aspect = aspect + + def get_size(self, renderer): + l1, l2 = self._axes.get_xlim() + rel_size = abs(l2-l1)*self._aspect + abs_size = 0. + return rel_size, abs_size + + class AxesY(_Base): + def __init__(self, axes, aspect=1.): + self._axes = axes + self._aspect = aspect + + def get_size(self, renderer): + l1, l2 = self._axes.get_ylim() + rel_size = abs(l2-l1)*self._aspect + abs_size = 0. + return rel_size, abs_size + + + class MaxExtent(_Base): + def __init__(self, artist_list, w_or_h): + self._artist_list = artist_list + + if w_or_h not in ["width", "height"]: + raise ValueError() + + self._w_or_h = w_or_h + + def add_artist(self, a): + self._artist_list.append(a) + + def get_size(self, renderer): + rel_size = 0. + w_list, h_list = [], [] + for a in self._artist_list: + bb = a.get_window_extent(renderer) + w_list.append(bb.width) + h_list.append(bb.height) + dpi = a.get_figure().get_dpi() + if self._w_or_h == "width": + abs_size = max(w_list)/dpi + elif self._w_or_h == "height": + abs_size = max(h_list)/dpi + + return rel_size, abs_size + + class Fraction(_Base): + def __init__(self, size, fraction): + self._size = size + self._fraction = fraction + + def get_size(self, renderer): + r, a = self._size.get_size(renderer) + rel_size = r*self._fraction + abs_size = a*self._fraction + return rel_size, abs_size + + class Padded(_Base): + def __init__(self, size, pad): + self._size = size + self._pad = pad + + def get_size(self, renderer): + r, a = self._size.get_size(renderer) + rel_size = r + abs_size = a + self._pad + return rel_size, abs_size + + + +class AxesLocator(object): + def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): + + self._axes_divider = axes_divider + + _xrefindex = axes_divider._xrefindex + _yrefindex = axes_divider._yrefindex + + self._nx, self._ny = nx - _xrefindex, ny - _yrefindex + + if nx1 is None: + nx1 = nx+1 + if ny1 is None: + ny1 = ny+1 + + self._nx1 = nx1 - _xrefindex + self._ny1 = ny1 - _yrefindex + + + def __call__(self, axes, renderer): + + _xrefindex = self._axes_divider._xrefindex + _yrefindex = self._axes_divider._yrefindex + + return self._axes_divider.locate(self._nx + _xrefindex, self._ny + _yrefindex, + self._nx1 + _xrefindex, self._ny1 + _yrefindex, + renderer) + + +class Divider(object): + + def __init__(self, fig, pos, horizontal, vertical, aspect=None, anchor="C"): + self._fig = fig + self._pos = pos + self._horizontal = horizontal + self._vertical = vertical + self._anchor = anchor + self._aspect = aspect + self._xrefindex = 0 + self._yrefindex = 0 + + + @staticmethod + def _calc_k(l, total_size, renderer): + + rs_sum, as_sum = 0., 0. + + for s in l: + rs, as = s.get_size(renderer) + rs_sum += rs + as_sum += as + + k = (total_size - as_sum) / rs_sum + return k + + + @staticmethod + def _calc_offsets(l, k, renderer): + + offsets = [0.] + + for s in l: + rs, as = s.get_size(renderer) + offsets.append(offsets[-1] + rs*k + as) + + return offsets + + + def set_position(self, pos): + self._pos = pos + + def get_position(self): + return self._pos + + def set_anchor(self, anchor): + """ + *anchor* + + ===== ============ + value description + ===== ============ + 'C' Center + 'SW' bottom left + 'S' bottom + 'SE' bottom right + 'E' right + 'NE' top right + 'N' top + 'NW' top left + 'W' left + ===== ============ + + """ + if anchor in mtransforms.Bbox.coefs.keys() or len(anchor) == 2: + self._anchor = anchor + else: + raise ValueError('argument must be among %s' % + ', '.join(mtransforms.BBox.coefs.keys())) + + + def set_horizontal(self, h): + self._horizontal = h + + def get_horizontal(self): + return self._horizontal + + def set_vertical(self, v): + self._vertical = v + + def get_vertical(self): + return self._vertical + + + def get_anchor(self): + return self._anchor + + + def set_aspect(self, aspect=False): + """ + *aspect* : True or False + """ + self._aspect = aspect + + def get_aspect(self): + return self._aspect + + + def locate(self, nx, ny, nx1=None, ny1=None, renderer=None): + + + figW,figH = self._fig.get_size_inches() + x, y, w, h = self.get_position() + + k_h = self._calc_k(self._horizontal, figW*w, renderer) + k_v = self._calc_k(self._vertical, figH*h, renderer) + + if self.get_aspect(): + k = min(k_h, k_v) + ox = self._calc_offsets(self._horizontal, k, renderer) + oy = self._calc_offsets(self._vertical, k, renderer) + else: + ox = self._calc_offsets(self._horizontal, k_h, renderer) + oy = self._calc_offsets(self._vertical, k_v, renderer) + + + ww = (ox[-1] - ox[0])/figW + hh = (oy[-1] - oy[0])/figH + pb = mtransforms.Bbox.from_bounds(x, y, w, h) + pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) + pb1_anchored = pb1.anchored(self.get_anchor(), pb) + + if nx1 is None: + nx1=nx+1 + if ny1 is None: + ny1=ny+1 + + x0, y0 = pb1_anchored.x0, pb1_anchored.y0 + x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW + y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH + + return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) + + + def new_locator(self, nx, ny, nx1=None, ny1=None): + return AxesLocator(self, nx, ny, nx1, ny1) + + +class SubplotDivider(Divider): + + def __init__(self, fig, *args, **kwargs): + """ + *fig* is a :class:`matplotlib.figure.Figure` instance. + + *args* is the tuple (*numRows*, *numCols*, *plotNum*), where + the array of subplots in the figure has dimensions *numRows*, + *numCols*, and where *plotNum* is the number of the subplot + being created. *plotNum* starts at 1 in the upper left + corner and increases to the right. + + If *numRows* <= *numCols* <= *plotNum* < 10, *args* can be the + decimal integer *numRows* * 100 + *numCols* * 10 + *plotNum*. + """ + + self.figure = fig + + if len(args)==1: + s = str(args[0]) + if len(s) != 3: + raise ValueError('Argument to subplot must be a 3 digits long') + rows, cols, num = map(int, s) + elif len(args)==3: + rows, cols, num = args + else: + raise ValueError( 'Illegal argument to subplot') + + + total = rows*cols + num -= 1 # convert from matlab to python indexing + # ie num in range(0,total) + if num >= total: + raise ValueError( 'Subplot number exceeds total subplots') + self._rows = rows + self._cols = cols + self._num = num + + self.update_params() + + pos = self.figbox.bounds + horizontal = kwargs.pop("horizontal", []) + vertical = kwargs.pop("vertical", []) + aspect = kwargs.pop("aspect", None) + anchor = kwargs.pop("anchor", "C") + + if kwargs: + raise Exception("") + + Divider.__init__(self, fig, pos, horizontal, vertical, + aspect=aspect, anchor=anchor) + + + def get_position(self): + self.update_params() + return self.figbox.bounds + + + def update_params(self): + 'update the subplot position from fig.subplotpars' + + rows = self._rows + cols = self._cols + num = self._num + + pars = self.figure.subplotpars + left = pars.left + right = pars.right + bottom = pars.bottom + top = pars.top + wspace = pars.wspace + hspace = pars.hspace + totWidth = right-left + totHeight = top-bottom + + figH = totHeight/(rows + hspace*(rows-1)) + sepH = hspace*figH + + figW = totWidth/(cols + wspace*(cols-1)) + sepW = wspace*figW + + rowNum, colNum = divmod(num, cols) + + figBottom = top - (rowNum+1)*figH - rowNum*sepH + figLeft = left + colNum*(figW + sepW) + + self.figbox = mtransforms.Bbox.from_bounds(figLeft, figBottom, + figW, figH) + +class AxesDivider(Divider): + + + def __init__(self, axes): + self._axes = axes + self._xref = Size.AxesX(axes) + self._yref = Size.AxesY(axes) + Divider.__init__(self, fig=axes.get_figure(), pos=None, + horizontal=[self._xref], vertical=[self._yref], + aspect=None, anchor="C") + + def new_horizontal(self, size, pad=None, pack_start=False): + + if pad: + if not isinstance(pad, Size._Base): + pad = Size.from_any(pad, + fraction_ref=self._xref) + if pack_start: + self._horizontal.insert(0, pad) + self._xrefindex += 1 + else: + self._horizontal.append(pad) + + if not isinstance(size, Size._Base): + size = Size.from_any(size, + fraction_ref=self._xref) + + if pack_start: + self._horizontal.insert(0, pad) + self._xrefindex += 1 + locator = self.new_locator(nx=0, ny=0) + else: + self._horizontal.append(size) + locator = self.new_locator(nx=len(self._horizontal)-1, ny=0) + + ax = LocatableAxes(self._axes.get_figure(), + self._axes.get_position(original=True)) + locator = self.new_locator(nx=len(self._horizontal)-1, ny=0) + ax.set_axes_locator(locator) + + return ax + + def new_vertical(self, size, pad=None, pack_start=False): + + if pad: + if not isinstance(pad, Size._Base): + pad = Size.from_any(pad, + fraction_ref=self._yref) + if pack_start: + self._vertical.insert(0, pad) + self._yrefindex += 1 + else: + self._vertical.append(pad) + + if not isinstance(size, Size._Base): + size = Size.from_any(size, + fraction_ref=self._yref) + + if pack_start: + self._vertical.insert(0, pad) + self._yrefindex += 1 + locator = self.new_locator(nx=0, ny=0) + else: + self._vertical.append(size) + locator = self.new_locator(nx=0, ny=len(self._vertical)-1) + + ax = LocatableAxes(self._axes.get_figure(), + self._axes.get_position(original=True)) + ax.set_axes_locator(locator) + + return ax + + + def get_aspect(self): + if self._aspect is None: + aspect = self._axes.get_aspect() + if aspect == "auto": + return False + else: + return True + else: + return self._aspect + + def get_position(self): + if self._pos is None: + bbox = self._axes.get_position(original=True) + return bbox.bounds + else: + return self._pos + + def get_anchor(self): + if self._anchor is None: + return self._axes.get_anchor() + else: + return self._anchor + + + +class LocatableAxesBase: + def __init__(self, *kl, **kw): + + self._axes_class.__init__(self, *kl, **kw) + + self._locator = None + self._locator_renderer = None + + def set_axes_locator(self, locator): + self._locator = locator + + def get_axes_locator(self): + return self._locator + + def apply_aspect(self, position=None): + + if self.get_axes_locator() is None: + self._axes_class.apply_apsect(self, position) + else: + pos = self.get_axes_locator()(self, self._locator_renderer) + self._axes_class.apply_aspect(self, position=pos) + + + def draw(self, renderer=None, inframe=False): + + self._locator_renderer = renderer + + self._axes_class.draw(self, renderer, inframe) + + + +_locatableaxes_classes = {} +def locatable_axes_factory(axes_class): + + new_class = _locatableaxes_classes.get(axes_class) + if new_class is None: + new_class = new.classobj("Locatable%s" % (axes_class.__name__), + (LocatableAxesBase, axes_class), + {'_axes_class': axes_class}) + _locatableaxes_classes[axes_class] = new_class + + return new_class + +if hasattr(maxes.Axes, "get_axes_locator"): + LocatableAxes = maxes.Axes +else: + LocatableAxes = locatable_axes_factory(maxes.Axes) + + +def make_axes_locatable(axes): + if not hasattr(axes, "set_axes_locator"): + new_class = locatable_axes_factory(type(axes)) + axes.__class__ = new_class + + divider = AxesDivider(axes) + locator = divider.new_locator(nx=0, ny=0) + axes.set_axes_locator(locator) + + return divider + + +def get_demo_image(): + # prepare image + delta = 0.5 + + extent = (-3,4,-4,3) + import numpy as np + x = np.arange(-3.0, 4.001, delta) + y = np.arange(-4.0, 3.001, delta) + X, Y = np.meshgrid(x, y) + import matplotlib.mlab as mlab + Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) + Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) + Z = (Z1 - Z2) * 10 + + return Z, extent + +def demo_locatable_axes(): + import matplotlib.pyplot as plt + + fig1 = plt.figure(1, (6, 6)) + fig1.clf() + + ## PLOT 1 + # simple image & colorbar + ax = fig1.add_subplot(2, 2, 1) + + Z, extent = get_demo_image() + + im = ax.imshow(Z, extent=extent, interpolation="nearest") + cb = plt.colorbar(im) + plt.setp(cb.ax.get_yticklabels(), visible=False) + + + ## PLOT 2 + # image and colorbar whose location is adjusted in the drawing time. + # a hard way + + divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) + + # axes for image + ax = LocatableAxes(fig1, divider.get_position()) + + # axes for coloarbar + ax_cb = LocatableAxes(fig1, divider.get_position()) + + h = [Size.AxesX(ax), # main axes + Size.Fixed(0.05), # padding, 0.1 inch + Size.Fixed(0.2), # colorbar, 0.3 inch + ] + + v = [Size.AxesY(ax)] + + divider.set_horizontal(h) + divider.set_vertical(v) + + ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) + ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) + + fig1.add_axes(ax) + fig1.add_axes(ax_cb) + + ax_cb.yaxis.set_ticks_position("right") + + Z, extent = get_demo_image() + + im = ax.imshow(Z, extent=extent, interpolation="nearest") + plt.colorbar(im, cax=ax_cb) + plt.setp(ax_cb.get_yticklabels(), visible=False) + + plt.draw() + #plt.colorbar(im, cax=ax_cb) + + + ## PLOT 3 + # image and colorbar whose location is adjusted in the drawing time. + # a easy way + + ax = fig1.add_subplot(2, 2, 3) + divider = make_axes_locatable(ax) + + ax_cb = divider.new_horizontal(size="5%", pad=0.05) + fig1.add_axes(ax_cb) + + im = ax.imshow(Z, extent=extent, interpolation="nearest") + plt.colorbar(im, cax=ax_cb) + plt.setp(ax_cb.get_yticklabels(), visible=False) + + + ## PLOT 4 + # two images side by sied with fixed padding. + + ax = fig1.add_subplot(2, 2, 4) + divider = make_axes_locatable(ax) + + ax2 = divider.new_horizontal(size="100%", pad=0.05) + fig1.add_axes(ax2) + + ax.imshow(Z, extent=extent, interpolation="nearest") + ax2.imshow(Z, extent=extent, interpolation="nearest") + plt.setp(ax2.get_yticklabels(), visible=False) + plt.draw() + +if __name__ == "__main__": + demo_locatable_axes() Added: trunk/matplotlib/examples/pylab_examples/axes_grid.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/axes_grid.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/axes_grid.py 2008-12-19 22:48:11 UTC (rev 6687) @@ -0,0 +1,343 @@ +import matplotlib.cbook as cbook + +import matplotlib.pyplot as plt + +from axes_divider import Size, SubplotDivider, LocatableAxes, Divider, get_demo_image + +class AxesGrid(object): + + def __init__(self, fig, rect, + nrows_ncols, + ngrids = None, + direction="row", + axes_pad = 0.02, + axes_class=None, + add_all=True, + share_all=False, + aspect=True, + label_mode="L", + colorbar_mode=None, + colorbar_location="right", + colorbar_pad=None, + colorbar_size="5%", + ): + + self._nrows, self._ncols = nrows_ncols + + if ngrids is None: + ngrids = self._nrows * self._ncols + else: + if (ngrids > self._nrows * self._ncols) or (ngrids <= 0): + raise Exception("") + + self.ngrids = ngrids + + self._axes_pad = axes_pad + + self._colorbar_mode = colorbar_mode + self._colorbar_location = colorbar_location + if colorbar_pad is None: + self._colorbar_pad = axes_pad + else: + self._colorbar_pad = colorbar_pad + + self._colorbar_size = colorbar_size + + if direction not in ["column", "row"]: + raise Exception("") + + self._direction = direction + + + if axes_class is None: + axes_class = LocatableAxes + + + self.axes_all = [] + self.axes_column = [[] for i in range(self._ncols)] + self.axes_row = [[] for i in range(self._nrows)] + + self.cbar_axes = [] + + h = [] + v = [] + if cbook.is_string_like(rect) or cbook.is_numlike(rect): + self._divider = SubplotDivider(fig, rect, horizontal=h, vertical=v, + aspect=aspect) + elif len(rect) == 3: + kw = dict(horizontal=h, vertical=v, aspect=aspect) + self._divider = SubplotDivider(fig, *rect, **kw) + elif len(rect) == 4: + self._divider = Divider(fig, rect, horizontal=h, vertical=v, + aspect=aspect) + else: + raise Exception("") + + + rect = self._divider.get_position() + + # reference axes + self._column_refax = [None for i in range(self._ncols)] + self._row_refax = [None for i in range(self._nrows)] + self._refax = None + + for i in range(self.ngrids): + + col, row = self.get_col_row(i) + + if share_all: + sharex = self._refax + sharey = self._refax + else: + sharex = self._column_refax[col] + sharey = self._row_refax[row] + + ax = axes_class(fig, rect, sharex=sharex, sharey=sharey) + + if share_all: + if self._refax is None: + self._refax = ax + else: + if sharex is None: + self._column_refax[col] = ax + if sharey is None: + self._row_refax[row] = ax + + self.axes_all.append(ax) + self.axes_column[col].append(ax) + self.axes_row[row].append(ax) + + cax = axes_class(fig, rect) + self.cbar_axes.append(cax) + + self.axes_llc = self.axes_column[0][-1] + + self._update_locators() + + if add_all: + for ax in self.axes_all+self.cbar_axes: + fig.add_axes(ax) + + self.set_label_mode(label_mode) + + + def _update_locators(self): + + h = [] + + h_ax_pos = [] + h_cb_pos = [] + for ax in self._column_refax: + if h: h.append(Size.Fixed(self._axes_pad)) + + h_ax_pos.append(len(h)) + + if ax: + sz = Size.AxesX(ax) + else: + sz = Size.AxesX(self.axes_llc) + h.append(sz) + + if self._colorbar_mode == "each" and self._colorbar_location == "right": + h.append(Size.from_any(self._colorbar_pad, sz)) + h_cb_pos.append(len(h)) + h.append(Size.from_any(self._colorbar_size, sz)) + + + v = [] + + v_ax_pos = [] + v_cb_pos = [] + for ax in self._row_refax[::-1]: + if v: v.append(Size.Fixed(self._axes_pad)) + v_ax_pos.append(len(v)) + if ax: + sz = Size.AxesY(ax) + else: + sz = Size.AxesY(self.axes_llc) + v.append(sz) + + + if self._colorbar_mode == "each" and self._colorbar_location == "top": + v.append(Size.from_any(self._colorbar_pad, sz)) + v_cb_pos.append(len(v)) + v.append(Size.from_any(self._colorbar_size, sz)) + + + for i in range(self.ngrids): + col, row = self.get_col_row(i) + #locator = self._divider.new_locator(nx=4*col, ny=2*(self._nrows - row - 1)) + locator = self._divider.new_locator(nx=h_ax_pos[col], + ny=v_ax_pos[self._nrows -1 - row]) + self.axes_all[i].set_axes_locator(locator) + + if self._colorbar_mode == "each": + if self._colorbar_location == "right": + locator = self._divider.new_locator(nx=h_cb_pos[col], + ny=v_ax_pos[self._nrows -1 - row]) + elif self._colorbar_location == "top": + locator = self._divider.new_locator(nx=h_ax_pos[col], + ny=v_cb_pos[self._nrows -1 - row]) + self.cbar_axes[i].set_axes_locator(locator) + + + if self._colorbar_mode == "single": + if self._colorbar_location == "right": + sz = Size.Fraction(Size.AxesX(self.axes_llc), self._nrows) + h.append(Size.from_any(self._colorbar_pad, sz)) + h.append(Size.from_any(self._colorbar_size, sz)) + locator = self._divider.new_locator(nx=-2, ny=0, ny1=-1) + elif self._colorbar_location == "top": + sz = Size.Fraction(Size.AxesY(self.axes_llc), self._ncols) + v.append(Size.from_any(self._colorbar_pad, sz)) + v.append(Size.from_any(self._colorbar_size, sz)) + locator = self._divider.new_locator(nx=0, nx1=-1, ny=-2) + for i in range(self.ngrids): + self.cbar_axes[i].set_visible(False) + self.cbar_axes[0].set_axes_locator(locator) + self.cbar_axes[0].set_visible(True) + elif self._colorbar_mode == "each": + for i in range(self.ngrids): + self.cbar_axes[i].set_visible(True) + else: + for i in range(self.ngrids): + self.cbar_axes[i].set_visible(False) + + self._divider.set_horizontal(h) + self._divider.set_vertical(v) + + + + def get_col_row(self, n): + if self._direction == "column": + col, row = divmod(n, self._nrows) + else: + row, col = divmod(n, self._ncols) + + return col, row + + + def __getitem__(self, i): + return self.axes_all[i] + + + def get_geometry(self): + return self._nrows, self._ncols + + def set_axes_pad(self, axes_pad): + self._axes_pad = axes_pad + + def get_axes_pad(self): + return self._axes_pad + + def set_aspect(self, aspect): + self._divider.set_aspect(aspect) + + def get_aspect(self): + return self._divider.get_aspect() + + def set_label_mode(self, mode): + if mode == "all": + for ax in self.axes_all: + [l.set_visible(True) for l in ax.get_xticklabels()] + [l.set_visible(True) for l in ax.get_yticklabels()] + elif mode == "L": + for ax in self.axes_column[0][:-1]: + [l.set_visible(False) for l in ax.get_xticklabels()] + [l.set_visible(True) for l in ax.get_yticklabels()] + ax = self.axes_column[0][-1] + [l.set_visible(True) for l in ax.get_xticklabels()] + [l.set_visible(True) for l in ax.get_yticklabels()] + for col in self.axes_column[1:]: + for ax in col[:-1]: + [l.set_visible(False) for l in ax.get_xticklabels()] + [l.set_visible(False) for l in ax.get_yticklabels()] + ax = col[-1] + [l.set_visible(True) for l in ax.get_xticklabels()] + [l.set_visible(False) for l in ax.get_yticklabels()] + elif mode == "1": + for ax in self.axes_all: + [l.set_visible(False) for l in ax.get_xticklabels()] + [l.set_visible(False) for l in ax.get_yticklabels()] + ax = self.axes_llc + [l.set_visible(True) for l in ax.get_xticklabels()] + [l.set_visible(True) for l in ax.get_yticklabels()] + + + +if __name__ == "__main__": + F = plt.figure(1, (9, 3.5)) + F.clf() + + F.subplots_adjust(left=0.05, right=0.98) + + grid = AxesGrid(F, 131, # similar to subplot(111) + nrows_ncols = (2, 2), + direction="row", + axes_pad = 0.05, + add_all=True, + label_mode = "1", + ) + + Z, extent = get_demo_image() + plt.ioff() + for i in range(4): + im = grid[i].imshow(Z, extent=extent, interpolation="nearest") + + # This only affects axes in first column and second row as share_all = False. + grid.axes_llc.set_xticks([-2, 0, 2]) + grid.axes_llc.set_yticks([-2, 0, 2]) + plt.ion() + + + grid = AxesGrid(F, 132, # similar to subplot(111) + nrows_ncols = (2, 2), + direction="row", + axes_pad = 0.0, + add_all=True, + share_all=True, + label_mode = "1", + colorbar_mode="single", + ) + + Z, extent = get_demo_image() + plt.ioff() + for i in range(4): + im = grid[i].imshow(Z, extent=extent, interpolation="nearest") + plt.colorbar(im, cax = grid.cbar_axes[0]) + plt.setp(grid.cbar_axes[0].get_yticklabels(), visible=False) + + # This affects all axes as share_all = True. + grid.axes_llc.set_xticks([-2, 0, 2]) + grid.axes_llc.set_yticks([-2, 0, 2]) + + plt.ion() + + + + grid = AxesGrid(F, 133, # similar to subplot(122) + nrows_ncols = (2, 2), + direction="row", + axes_pad = 0.1, + add_all=True, + label_mode = "1", + share_all = True, + colorbar_location="top", + colorbar_mode="each", + colorbar_size="7%", + colorbar_pad="2%", + ) + plt.ioff() + for i in range(4): + im = grid[i].imshow(Z, extent=extent, interpolation="nearest") + plt.colorbar(im, cax = grid.cbar_axes[i], + orientation="horizontal") + grid.cbar_axes[i].xaxis.set_ticks_position("top") + plt.setp(grid.cbar_axes[i].get_xticklabels(), visible=False) + + # This affects all axes as share_all = True. + grid.axes_llc.set_xticks([-2, 0, 2]) + grid.axes_llc.set_yticks([-2, 0, 2]) + + plt.ion() + plt.draw() Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-12-19 21:41:11 UTC (rev 6686) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-12-19 22:48:11 UTC (rev 6687) @@ -545,6 +545,8 @@ self.set_navigate(True) self.set_navigate_mode(None) + self._axes_locator = None + if len(kwargs): martist.setp(self, **kwargs) if self.xaxis is not None: @@ -793,6 +795,21 @@ pos = self.get_position(original=True) self.set_position(pos, which='active') + def set_axes_locator(self, locator): + """ + set axes_locator + + ACCEPT : a callable object which takes an axes instance and renderer and + returns a bbox. + """ + self._axes_locator = locator + + def get_axes_locator(self): + """ + return axes_locator + """ + return self._axes_locator + def _set_artist_props(self, a): 'set the boilerplate props for artists added to axes' a.set_figure(self.figure) @@ -1531,7 +1548,12 @@ if not self.get_visible(): return renderer.open_group('axes') - self.apply_aspect() + locator = self.get_axes_locator() + if locator: + pos = locator(self, renderer) + self.apply_aspect(pos) + else: + self.apply_aspect() # the patch draws the background rectangle -- the frame below # will draw the edges This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-21 04:14:24
|
Revision: 6690 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6690&view=rev Author: leejjoon Date: 2008-12-21 04:14:20 +0000 (Sun, 21 Dec 2008) Log Message: ----------- Merged revisions 6688-6689 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6688 | leejjoon | 2008-12-20 22:46:05 -0500 (Sat, 20 Dec 2008) | 1 line fix hatch bug in pdf backend ........ r6689 | leejjoon | 2008-12-20 23:07:26 -0500 (Sat, 20 Dec 2008) | 1 line fix dpi dependent offset of Shadow ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/examples/pylab_examples/hatch_demo.py trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/patches.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6685 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6689 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-21 04:07:26 UTC (rev 6689) +++ trunk/matplotlib/CHANGELOG 2008-12-21 04:14:20 UTC (rev 6690) @@ -1,8 +1,13 @@ +2008-12-20 fix the dpi-dependent offset of Shadow. - JJL + +2008-12-20 fix the hatch bug in the pdf backend. minor update + in docs and example - JJL + 2008-12-19 Add axes_locator attribute in Axes. Two examples are added. - JJL -2008-12-19 Update Axes.legend documnetation. /api/api_changes.rst is also - updated to describe chages in keyword parameters. +2008-12-19 Update Axes.legend documnetation. /api/api_changes.rst is also + updated to describe chages in keyword parameters. Issue a warning if old keyword parameters are used. - JJL 2008-12-18 add new arrow style, a line + filled triangles. -JJL Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-21 04:07:26 UTC (rev 6689) +++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-21 04:14:20 UTC (rev 6690) @@ -1,18 +1,25 @@ """ -Hatching (pattern filled polygons) is supported currently on PS +Hatching (pattern filled polygons) is supported currently on PS and PDF backend only. See the set_patch method in http://matplotlib.sf.net/matplotlib.patches.html#Patch for details """ -import matplotlib -matplotlib.use('PS') -from pylab import figure +import matplotlib.pyplot as plt -fig = figure() -ax = fig.add_subplot(111) -bars = ax.bar(range(1,5), range(1,5), color='gray', ecolor='black') +fig = plt.figure() +ax1 = fig.add_subplot(121) +ax1.annotate("Hatch is only supported in the PS and PDF backend", (1, 1), + xytext=(0, 5), + xycoords="axes fraction", textcoords="offset points", ha="center" + ) +ax1.bar(range(1,5), range(1,5), color='gray', ecolor='black', hatch="/") + +ax2 = fig.add_subplot(122) +bars = ax2.bar(range(1,5), range(1,5), color='gray', ecolor='black') + patterns = ('/', '+', 'x', '\\') for bar, pattern in zip(bars, patterns): bar.set_hatch(pattern) -fig.savefig('hatch4.ps') + +plt.show() Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-21 04:07:26 UTC (rev 6689) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-21 04:14:20 UTC (rev 6690) @@ -942,7 +942,7 @@ def hatchPattern(self, lst): pattern = self.hatchPatterns.get(lst, None) if pattern is not None: - return pattern[0] + return pattern name = Name('H%d' % self.nextHatch) self.nextHatch += 1 @@ -1233,7 +1233,7 @@ def get_image_magnification(self): return self.image_dpi/72.0 - + def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): # MGDTODO: Support clippath here gc = self.new_gc() Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-12-21 04:07:26 UTC (rev 6689) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-12-21 04:14:20 UTC (rev 6690) @@ -247,12 +247,12 @@ CURRENT LIMITATIONS: - 1. Hatching is supported in the PostScript backend only. + 1. Hatching is supported in the PostScript and the PDF backend only. 2. Hatching is done with solid black lines of width 0. - ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] + ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] (ps & pdf backend only) """ self._hatch = h @@ -373,7 +373,7 @@ self.patch = patch self.props = props self._ox, self._oy = ox, oy - self._update_transform() + self._shadow_transform = transforms.Affine2D() self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -391,20 +391,20 @@ self.set_facecolor((r,g,b,0.5)) self.set_edgecolor((r,g,b,0.5)) - def _update_transform(self): - self._shadow_transform = transforms.Affine2D().translate(self._ox, self._oy) + def _update_transform(self, renderer): + ox = renderer.points_to_pixels(self._ox) + oy = renderer.points_to_pixels(self._oy) + self._shadow_transform.clear().translate(ox, oy) def _get_ox(self): return self._ox def _set_ox(self, ox): self._ox = ox - self._update_transform() def _get_oy(self): return self._oy def _set_oy(self, oy): self._oy = oy - self._update_transform() def get_path(self): return self.patch.get_path() @@ -412,6 +412,10 @@ def get_patch_transform(self): return self.patch.get_patch_transform() + self._shadow_transform + def draw(self, renderer): + self._update_transform(renderer) + Patch.draw(self, renderer) + class Rectangle(Patch): """ Draw a rectangle with lower left at *xy* = (*x*, *y*) with This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-23 19:49:18
|
Revision: 6699 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6699&view=rev Author: jdh2358 Date: 2008-12-23 19:49:08 +0000 (Tue, 23 Dec 2008) Log Message: ----------- added support for mincnt to hexbin Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/release/osx/Makefile Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-12-23 16:06:15 UTC (rev 6698) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-12-23 19:49:08 UTC (rev 6699) @@ -5212,7 +5212,7 @@ xscale = 'linear', yscale = 'linear', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, edgecolors='none', - reduce_C_function = np.mean, + reduce_C_function = np.mean, mincnt=None, **kwargs): """ call signature:: @@ -5221,7 +5221,7 @@ xscale = 'linear', yscale = 'linear', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, edgecolors='none' - reduce_C_function = np.mean, + reduce_C_function = np.mean, mincnt=None, **kwargs) Make a hexagonal binning plot of *x* versus *y*, where *x*, @@ -5269,6 +5269,10 @@ *scale*: [ 'linear' | 'log' ] Use a linear or log10 scale on the vertical axis. + *mincnt*: None | a positive integer + If not None, only display cells with at least *mincnt* + number of points in the cell + Other keyword arguments controlling color mapping and normalization arguments: @@ -5369,6 +5373,8 @@ d1 = (x-ix1)**2 + 3.0 * (y-iy1)**2 d2 = (x-ix2-0.5)**2 + 3.0 * (y-iy2-0.5)**2 bdist = (d1<d2) + if mincnt is None: + mincnt = 0 if C is None: accum = np.zeros(n) @@ -5400,10 +5406,11 @@ else: lattice2[ix2[i], iy2[i]].append( C[i] ) + for i in xrange(nx1): for j in xrange(ny1): vals = lattice1[i,j] - if len(vals): + if len(vals)>mincnt: lattice1[i,j] = reduce_C_function( vals ) else: lattice1[i,j] = np.nan Modified: trunk/matplotlib/release/osx/Makefile =================================================================== --- trunk/matplotlib/release/osx/Makefile 2008-12-23 16:06:15 UTC (rev 6698) +++ trunk/matplotlib/release/osx/Makefile 2008-12-23 19:49:08 UTC (rev 6699) @@ -95,7 +95,7 @@ rm -rf upload &&\ mkdir upload &&\ cp matplotlib-${MPLVERSION}.tar.gz upload/ &&\ - cp matplotlib-${MPLVERSION}/dist/matplotlib-${MPLVERSION}_r0-py2.5-macosx-10.3-fat.egg upload/matplotlib-${MPLVERSION}-py2.5.egg &&\ + cp matplotlib-${MPLVERSION}/dist/matplotlib-${MPLVERSION}_r0-py2.5-macosx-10.3-fat.egg upload/matplotlib-${MPLVERSION}-macosx-py2.5.egg &&\ cp matplotlib-${MPLVERSION}/dist/matplotlib-${MPLVERSION}-py2.5-macosx10.5.zip upload/matplotlib-${MPLVERSION}-py2.5-mpkg.zip&&\ scp upload/* jd...@fr...:uploads/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-26 16:28:07
|
Revision: 6702 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6702&view=rev Author: mdboom Date: 2008-12-26 16:28:04 +0000 (Fri, 26 Dec 2008) Log Message: ----------- Merge branch 'mathdefault' Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/config/mplconfig.py trunk/matplotlib/lib/matplotlib/config/rcsetup.py trunk/matplotlib/lib/matplotlib/mathtext.py trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/matplotlibrc.template Modified: trunk/matplotlib/lib/matplotlib/config/mplconfig.py =================================================================== --- trunk/matplotlib/lib/matplotlib/config/mplconfig.py 2008-12-23 21:55:45 UTC (rev 6701) +++ trunk/matplotlib/lib/matplotlib/config/mplconfig.py 2008-12-26 16:28:04 UTC (rev 6702) @@ -168,6 +168,7 @@ bf = T.Trait('serif:bold' , mplT.FontconfigPatternHandler()) sf = T.Trait('sans' , mplT.FontconfigPatternHandler()) fontset = T.Trait('cm', 'cm', 'stix', 'stixsans', 'custom') + default = T.Trait(*("rm cal it tt sf bf default bb frak circled scr regular".split())) fallback_to_cm = T.true class axes(TConfig): Modified: trunk/matplotlib/lib/matplotlib/config/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2008-12-23 21:55:45 UTC (rev 6701) +++ trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2008-12-26 16:28:04 UTC (rev 6702) @@ -209,6 +209,9 @@ validate_fontset = ValidateInStrings('fontset', ['cm', 'stix', 'stixsans', 'custom']) +validate_mathtext_default = ValidateInStrings( + 'default', "rm cal it tt sf bf default bb frak circled scr regular".split()) + validate_verbose = ValidateInStrings('verbose',[ 'silent', 'helpful', 'debug', 'debug-annoying', ]) @@ -371,6 +374,7 @@ 'mathtext.bf' : ['serif:bold', validate_font_properties], 'mathtext.sf' : ['sans\-serif', validate_font_properties], 'mathtext.fontset' : ['cm', validate_fontset], + 'mathtext.default' : ['it', validate_mathtext_default], 'mathtext.fallback_to_cm' : [True, validate_bool], 'image.aspect' : ['equal', validate_aspect], # equal, auto, a number Modified: trunk/matplotlib/lib/matplotlib/mathtext.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mathtext.py 2008-12-23 21:55:45 UTC (rev 6701) +++ trunk/matplotlib/lib/matplotlib/mathtext.py 2008-12-26 16:28:04 UTC (rev 6702) @@ -403,7 +403,7 @@ *fontX*: one of the TeX font names:: - tt, it, rm, cal, sf, bf or default (non-math) + tt, it, rm, cal, sf, bf or default/regular (non-math) *fontclassX*: TODO @@ -419,7 +419,7 @@ """ *font*: one of the TeX font names:: - tt, it, rm, cal, sf, bf or default (non-math) + tt, it, rm, cal, sf, bf or default/regular (non-math) *font_class*: TODO @@ -543,6 +543,7 @@ filename = findfont(default_font_prop) default_font = self.CachedFont(FT2Font(str(filename))) self._fonts['default'] = default_font + self._fonts['regular'] = default_font def destroy(self): self.glyphd = None @@ -616,7 +617,7 @@ pclt = cached_font.font.get_sfnt_table('pclt') if pclt is None: # Some fonts don't store the xHeight, so we do a poor man's xHeight - metrics = self.get_metrics(font, 'it', 'x', fontsize, dpi) + metrics = self.get_metrics(font, rcParams['mathtext.default'], 'x', fontsize, dpi) return metrics.iceberg xHeight = (pclt['xHeight'] / 64.0) * (fontsize / 12.0) * (dpi / 100.0) return xHeight @@ -936,7 +937,7 @@ elif not doing_sans_conversion: # This will generate a dummy character uniindex = 0x1 - fontname = 'it' + fontname = rcParams['mathtext.default'] # Handle private use area glyphs if (fontname in ('it', 'rm', 'bf') and @@ -1007,6 +1008,7 @@ default_font.fname = filename self.fonts['default'] = default_font + self.fonts['regular'] = default_font self.pswriter = StringIO() def _get_font(self, font): @@ -2064,7 +2066,7 @@ _dropsub_symbols = set(r'''\int \oint'''.split()) - _fontnames = set("rm cal it tt sf bf default bb frak circled scr".split()) + _fontnames = set("rm cal it tt sf bf default bb frak circled scr regular".split()) _function_names = set(""" arccos csc ker min arcsin deg lg Pr arctan det lim sec arg dim @@ -2294,7 +2296,7 @@ def _get_font(self): return self._font def _set_font(self, name): - if name in ('it', 'rm', 'bf'): + if name in Parser._fontnames: self.font_class = name self._font = name font = property(_get_font, _set_font) @@ -2336,7 +2338,7 @@ hlist = Hlist(symbols) # We're going into math now, so set font to 'it' self.push_state() - self.get_state().font = 'it' + self.get_state().font = rcParams['mathtext.default'] return [hlist] def _make_space(self, percentage): @@ -2346,7 +2348,7 @@ width = self._em_width_cache.get(key) if width is None: metrics = state.font_output.get_metrics( - state.font, 'it', 'm', state.fontsize, state.dpi) + state.font, rcParams['mathtext.default'], 'm', state.fontsize, state.dpi) width = metrics.advance self._em_width_cache[key] = width return Kern(width * percentage) @@ -2665,7 +2667,7 @@ # Shift so the fraction line sits in the middle of the # equals sign metrics = state.font_output.get_metrics( - state.font, 'it', '=', state.fontsize, state.dpi) + state.font, rcParams['mathtext.default'], '=', state.fontsize, state.dpi) shift = (cden.height - ((metrics.ymax + metrics.ymin) / 2 - thickness * 3.0)) Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-23 21:55:45 UTC (rev 6701) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-26 16:28:04 UTC (rev 6702) @@ -233,6 +233,9 @@ validate_fontset = ValidateInStrings('fontset', ['cm', 'stix', 'stixsans', 'custom']) +validate_mathtext_default = ValidateInStrings( + 'default', "rm cal it tt sf bf default bb frak circled scr regular".split()) + validate_verbose = ValidateInStrings('verbose',[ 'silent', 'helpful', 'debug', 'debug-annoying', ]) @@ -397,6 +400,7 @@ 'mathtext.bf' : ['serif:bold', validate_font_properties], 'mathtext.sf' : ['sans\-serif', validate_font_properties], 'mathtext.fontset' : ['cm', validate_fontset], + 'mathtext.default' : ['it', validate_mathtext_default], 'mathtext.fallback_to_cm' : [True, validate_bool], 'image.aspect' : ['equal', validate_aspect], # equal, auto, a number Modified: trunk/matplotlib/matplotlibrc.template =================================================================== --- trunk/matplotlib/matplotlibrc.template 2008-12-23 21:55:45 UTC (rev 6701) +++ trunk/matplotlib/matplotlibrc.template 2008-12-26 16:28:04 UTC (rev 6702) @@ -185,6 +185,11 @@ # fonts when a symbol can not be found in one of # the custom math fonts. +#mathtext.default : it # The default font to use for math. + # Can be any of the LaTeX font names, including + # the special name "regular" for the same font + # used in regular text. + ### AXES # default face and edge color, default tick sizes, # default fontsizes for ticklabels, and so on. See This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2008-12-29 13:49:02
|
Revision: 6706 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6706&view=rev Author: jouni Date: 2008-12-29 13:48:51 +0000 (Mon, 29 Dec 2008) Log Message: ----------- Fix a bug in pdf usetex support Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-28 16:07:10 UTC (rev 6705) +++ trunk/matplotlib/CHANGELOG 2008-12-29 13:48:51 UTC (rev 6706) @@ -1,3 +1,7 @@ +2008-12-29 Fix a bug in pdf usetex support, which occurred if the same + Type-1 font was used with different encodings, e.g. with + Minion Pro and MnSymbol. - JKS + 2008-12-20 fix the dpi-dependent offset of Shadow. - JJL 2008-12-20 fix the hatch bug in the pdf backend. minor update Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-28 16:07:10 UTC (rev 6705) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-29 13:48:51 UTC (rev 6706) @@ -394,10 +394,11 @@ 'Contents': contentObject } self.writeObject(thePageObject, thePage) - # self.fontNames maps filenames to internal font names - self.fontNames = {} + self.fontNames = {} # maps filenames to internal font names self.nextFont = 1 # next free internal font name - self.fontInfo = {} # information on fonts: metrics, encoding + self.dviFontInfo = {} # information on dvi fonts + self.type1Descriptors = {} # differently encoded Type-1 fonts may + # share the same descriptor self.alphaStates = {} # maps alpha values to graphics state objects self.nextAlphaState = 1 @@ -474,7 +475,7 @@ """ Select a font based on fontprop and return a name suitable for Op.selectfont. If fontprop is a string, it will be interpreted - as the filename of the font. + as the filename (or dvi name) of the font. """ if is_string_like(fontprop): @@ -496,17 +497,18 @@ fonts = {} for filename, Fx in self.fontNames.items(): if filename.endswith('.afm'): + # from pdf.use14corefonts fontdictObject = self._write_afm_font(filename) - elif filename.endswith('.pfb') or filename.endswith('.pfa'): - # a Type 1 font; limited support for now - fontdictObject = self.embedType1(filename, self.fontInfo[Fx]) + elif self.dviFontInfo.has_key(filename): + # a Type 1 font from a dvi file + fontdictObject = self.embedType1(filename, self.dviFontInfo[filename]) else: + # a normal TrueType font realpath, stat_key = get_realpath_and_stat(filename) chars = self.used_characters.get(stat_key) if chars is not None and len(chars[1]): fontdictObject = self.embedTTF(realpath, chars[1]) fonts[Fx] = fontdictObject - #print >>sys.stderr, filename self.writeObject(self.fontObject, fonts) def _write_afm_font(self, filename): @@ -522,36 +524,40 @@ self.writeObject(fontdictObject, fontdict) return fontdictObject - def embedType1(self, filename, fontinfo): + def embedType1(self, texname, fontinfo): # TODO: font effects such as SlantFont - fh = open(filename, 'rb') matplotlib.verbose.report( - 'Embedding Type 1 font ' + filename, 'debug') - try: - fontdata = fh.read() - finally: - fh.close() + 'Embedding Type 1 font ' + fontinfo.fontfile + + ' with encoding ' + fontinfo.encodingfile, 'debug') - font = FT2Font(filename) + # Use FT2Font to get several font properties + font = FT2Font(fontinfo.fontfile) - widthsObject, fontdescObject, fontdictObject, fontfileObject = \ - [ self.reserveObject(n) for n in - ('font widths', 'font descriptor', - 'font dictionary', 'font file') ] + # Font descriptors may be shared between differently encoded + # Type-1 fonts, so only create a new descriptor if there is no + # existing descriptor for this font. + fontdesc = self.type1Descriptors.get(fontinfo.fontfile) + if fontdesc is None: + fontdesc = self.createType1Descriptor(font, fontinfo.fontfile) + self.type1Descriptors[fontinfo.fontfile] = fontdesc - firstchar = 0 - lastchar = len(fontinfo.widths) - 1 + # Widths + widthsObject = self.reserveObject('font widths') + self.writeObject(widthsObject, fontinfo.widths) + # Font dictionary + fontdictObject = self.reserveObject('font dictionary') fontdict = { 'Type': Name('Font'), 'Subtype': Name('Type1'), 'BaseFont': Name(font.postscript_name), 'FirstChar': 0, - 'LastChar': lastchar, + 'LastChar': len(fontinfo.widths) - 1, 'Widths': widthsObject, - 'FontDescriptor': fontdescObject, + 'FontDescriptor': fontdesc, } + # Encoding (if needed) if fontinfo.encodingfile is not None: enc = dviread.Encoding(fontinfo.encodingfile) differencesArray = [ Name(ch) for ch in enc ] @@ -561,6 +567,15 @@ 'Differences': differencesArray }, }) + self.writeObject(fontdictObject, fontdict) + return fontdictObject + + def createType1Descriptor(self, font, fontfile): + # Create and write the font descriptor and the font file + # of a Type-1 font + fontdescObject = self.reserveObject('font descriptor') + fontfileObject = self.reserveObject('font file') + _, _, fullname, familyname, weight, italic_angle, fixed_pitch, \ ul_position, ul_thickness = font.get_ps_font_info() @@ -591,11 +606,9 @@ #'FontWeight': a number where 400 = Regular, 700 = Bold } - self.writeObject(fontdictObject, fontdict) - self.writeObject(widthsObject, fontinfo.widths) self.writeObject(fontdescObject, descriptor) - t1font = type1font.Type1Font(filename) + t1font = type1font.Type1Font(fontfile) self.beginStream(fontfileObject.id, None, { 'Length1': len(t1font.parts[0]), 'Length2': len(t1font.parts[1]), @@ -604,7 +617,7 @@ self.currentstream.write(t1font.parts[1]) self.endStream() - return fontdictObject + return fontdescObject def _get_xobject_symbol_name(self, filename, symbol_name): return "%s-%s" % ( @@ -1362,13 +1375,15 @@ oldfont, seq = None, [] for x1, y1, dvifont, glyph, width in page.text: if dvifont != oldfont: - psfont = self.tex_font_mapping(dvifont.texname) - pdfname = self.file.fontName(psfont.filename) - if self.file.fontInfo.get(pdfname, None) is None: - self.file.fontInfo[pdfname] = Bunch( + pdfname = self.file.fontName(dvifont.texname) + if not self.file.dviFontInfo.has_key(dvifont.texname): + psfont = self.tex_font_mapping(dvifont.texname) + self.file.dviFontInfo[dvifont.texname] = Bunch( + fontfile=psfont.filename, encodingfile=psfont.encoding, widths=dvifont.widths, dvifont=dvifont) + # TODO: font effects seq += [['font', pdfname, dvifont.size]] oldfont = dvifont seq += [['text', x1, y1, [chr(glyph)], x1+width]] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-29 14:08:16
|
Revision: 6708 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6708&view=rev Author: mdboom Date: 2008-12-29 14:08:13 +0000 (Mon, 29 Dec 2008) Log Message: ----------- Merge branch 'hatching' Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/hatch_demo.py trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/backends/backend_agg.py trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/backends/backend_ps.py trunk/matplotlib/lib/matplotlib/backends/backend_svg.py trunk/matplotlib/lib/matplotlib/path.py trunk/matplotlib/src/_backend_agg.cpp trunk/matplotlib/src/_backend_agg.h Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -12,7 +12,7 @@ xytext=(0, 5), xycoords="axes fraction", textcoords="offset points", ha="center" ) -ax1.bar(range(1,5), range(1,5), color='gray', ecolor='black', hatch="/") +ax1.bar(range(1,5), range(1,5), color='gray', edgecolor='red', hatch="/") ax2 = fig.add_subplot(122) @@ -23,3 +23,5 @@ bar.set_hatch(pattern) plt.show() +plt.savefig("test.pdf") +plt.savefig("test.ps") Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -30,6 +30,7 @@ import matplotlib.colors as colors import matplotlib.transforms as transforms import matplotlib.widgets as widgets +import matplotlib.path as path from matplotlib import rcParams class RendererBase: @@ -679,6 +680,14 @@ """ return self._hatch + def get_hatch_path(self, density=6.0): + """ + Returns a Path for the current hatch. + """ + if self._hatch is None: + return None + return path.Path.hatch(self._hatch, density) + class Event: """ A matplotlib event. Attach additional attributes as defined in Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -73,9 +73,13 @@ 'debug-annoying') def draw_path(self, gc, path, transform, rgbFace=None): + """ + Draw the path + """ nmax = rcParams['agg.path.chunksize'] # here at least for testing npts = path.vertices.shape[0] - if nmax > 100 and npts > nmax and path.should_simplify and rgbFace is None: + if (nmax > 100 and npts > nmax and path.should_simplify and + rgbFace is None and gc.get_hatch() is None): nch = npy.ceil(npts/float(nmax)) chsize = int(npy.ceil(npts/nch)) i0 = npy.arange(0, npts, chsize) @@ -93,7 +97,6 @@ else: self._renderer.draw_path(gc, path, transform, rgbFace) - def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -953,21 +953,20 @@ 'CA': alpha, 'ca': alpha }) return name - def hatchPattern(self, lst): - pattern = self.hatchPatterns.get(lst, None) + def hatchPattern(self, hatch_style): + pattern = self.hatchPatterns.get(hatch_style, None) if pattern is not None: return pattern name = Name('H%d' % self.nextHatch) self.nextHatch += 1 - self.hatchPatterns[lst] = name + self.hatchPatterns[hatch_style] = name return name def writeHatches(self): hatchDict = dict() - sidelen = 144.0 - density = 24.0 - for lst, name in self.hatchPatterns.items(): + sidelen = 72.0 + for hatch_style, name in self.hatchPatterns.items(): ob = self.reserveObject('hatch pattern') hatchDict[name] = ob res = { 'Procsets': @@ -983,33 +982,21 @@ # lst is a tuple of stroke color, fill color, # number of - lines, number of / lines, # number of | lines, number of \ lines - rgb = lst[0] + rgb = hatch_style[0] self.output(rgb[0], rgb[1], rgb[2], Op.setrgb_stroke) - if lst[1] is not None: - rgb = lst[1] + if hatch_style[1] is not None: + rgb = hatch_style[1] self.output(rgb[0], rgb[1], rgb[2], Op.setrgb_nonstroke, 0, 0, sidelen, sidelen, Op.rectangle, Op.fill) - if lst[2]: # - - for j in npy.arange(0.0, sidelen, density/lst[2]): - self.output(0, j, Op.moveto, - sidelen, j, Op.lineto) - if lst[3]: # / - for j in npy.arange(0.0, sidelen, density/lst[3]): - self.output(0, j, Op.moveto, - sidelen-j, sidelen, Op.lineto, - sidelen-j, 0, Op.moveto, - sidelen, j, Op.lineto) - if lst[4]: # | - for j in npy.arange(0.0, sidelen, density/lst[4]): - self.output(j, 0, Op.moveto, - j, sidelen, Op.lineto) - if lst[5]: # \ - for j in npy.arange(sidelen, 0.0, -density/lst[5]): - self.output(sidelen, j, Op.moveto, - j, sidelen, Op.lineto, - j, 0, Op.moveto, - 0, j, Op.lineto) + + self.output(0.1, Op.setlinewidth) + + # TODO: We could make this dpi-dependent, but that would be + # an API change + self.output(*self.pathOperations( + Path.hatch(hatch_style[2]), + Affine2D().scale(sidelen))) self.output(Op.stroke) self.endStream() @@ -1735,13 +1722,8 @@ return [Name('DeviceRGB'), Op.setcolorspace_nonstroke] else: hatch = hatch.lower() - lst = ( self._rgb, - self._fillcolor, - hatch.count('-') + hatch.count('+'), - hatch.count('/') + hatch.count('x'), - hatch.count('|') + hatch.count('+'), - hatch.count('\\') + hatch.count('x') ) - name = self.file.hatchPattern(lst) + hatch_style = (self._rgb, self._fillcolor, hatch) + name = self.file.hatchPattern(hatch_style) return [Name('Pattern'), Op.setcolorspace_nonstroke, name, Op.setcolor_nonstroke] Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -31,7 +31,7 @@ from matplotlib._mathtext_data import uni2type1 from matplotlib.text import Text from matplotlib.path import Path -from matplotlib.transforms import IdentityTransform +from matplotlib.transforms import Affine2D import numpy as npy import binascii @@ -163,7 +163,7 @@ self.linedash = None self.fontname = None self.fontsize = None - self.hatch = None + self._hatches = {} self.image_magnification = imagedpi/72.0 self._clip_paths = {} self._path_collection_id = 0 @@ -231,58 +231,36 @@ if store: self.fontname = fontname if store: self.fontsize = fontsize - def set_hatch(self, hatch): - """ - hatch can be one of: - / - diagonal hatching - \ - back diagonal - | - vertical - - - horizontal - + - crossed - X - crossed diagonal + def create_hatch(self, hatch): + sidelen = 72 + if self._hatches.has_key(hatch): + return self._hatches[hatch] + name = 'H%d' % len(self._hatches) + self._pswriter.write("""\ + << /PatternType 1 + /PaintType 2 + /TilingType 2 + /BBox[0 0 %(sidelen)d %(sidelen)d] + /XStep %(sidelen)d + /YStep %(sidelen)d - letters can be combined, in which case all the specified - hatchings are done + /PaintProc { + pop + 0 setlinewidth +""" % locals()) + self._pswriter.write( + self._convert_path(Path.hatch(hatch), Affine2D().scale(72.0))) + self._pswriter.write("""\ + stroke + } bind + >> + matrix + makepattern + /%(name)s exch def +""" % locals()) + self._hatches[hatch] = name + return name - if same letter repeats, it increases the density of hatching - in that direction - """ - hatches = {'horiz':0, 'vert':0, 'diag1':0, 'diag2':0} - - for letter in hatch: - if (letter == '/'): hatches['diag2'] += 1 - elif (letter == '\\'): hatches['diag1'] += 1 - elif (letter == '|'): hatches['vert'] += 1 - elif (letter == '-'): hatches['horiz'] += 1 - elif (letter == '+'): - hatches['horiz'] += 1 - hatches['vert'] += 1 - elif (letter.lower() == 'x'): - hatches['diag1'] += 1 - hatches['diag2'] += 1 - - def do_hatch(angle, density): - if (density == 0): return "" - return """\ - gsave - eoclip %s rotate 0.0 0.0 0.0 0.0 setrgbcolor 0 setlinewidth - /hatchgap %d def - pathbbox /hatchb exch def /hatchr exch def /hatcht exch def /hatchl exch def - hatchl cvi hatchgap idiv hatchgap mul - hatchgap - hatchr cvi hatchgap idiv hatchgap mul - {hatcht m 0 hatchb hatcht sub r } - for - stroke - grestore - """ % (angle, 12/density) - self._pswriter.write("gsave\n") - self._pswriter.write(do_hatch(90, hatches['horiz'])) - self._pswriter.write(do_hatch(0, hatches['vert'])) - self._pswriter.write(do_hatch(45, hatches['diag1'])) - self._pswriter.write(do_hatch(-45, hatches['diag2'])) - self._pswriter.write("grestore\n") - def get_canvas_width_height(self): 'return the canvas width and height in display coords' return self.width, self.height @@ -816,15 +794,17 @@ if fill: if stroke: write("gsave\n") - self.set_color(store=0, *rgbFace[:3]) - write("fill\ngrestore\n") - else: - self.set_color(store=0, *rgbFace[:3]) - write("fill\n") + self.set_color(store=0, *rgbFace[:3]) + write("fill\n") + if stroke: + write("grestore\n") hatch = gc.get_hatch() if hatch: - self.set_hatch(hatch) + hatch_name = self.create_hatch(hatch) + write("gsave\n") + write("[/Pattern [/DeviceRGB]] setcolorspace %f %f %f " % gc.get_rgb()[:3]) + write("%s setcolor fill grestore\n" % hatch_name) if stroke: write("stroke\n") Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -57,6 +57,7 @@ self._markers = {} self._path_collection_id = 0 self._imaged = {} + self._hatchd = {} self.mathtext_parser = MathTextParser('SVG') svgwriter.write(svgProlog%(width,height,width,height)) @@ -90,15 +91,38 @@ font.set_size(size, 72.0) return font + def _get_hatch(self, gc, rgbFace): + """ + Create a new hatch pattern + """ + HATCH_SIZE = 144 + dictkey = (gc.get_hatch().lower(), rgbFace, gc.get_rgb()) + id = self._hatchd.get(dictkey) + if id is None: + id = 'h%s' % md5(str(dictkey)).hexdigest() + self._svgwriter.write('<defs>\n <pattern id="%s" ' % id) + self._svgwriter.write('patternUnits="userSpaceOnUse" x="0" y="0" ') + self._svgwriter.write(' width="%d" height="%d" >\n' % (HATCH_SIZE, HATCH_SIZE)) + path_data = self._convert_path(gc.get_hatch_path(), Affine2D().scale(144)) + path = '<path d="%s" fill="%s" stroke="%s" stroke-width="1.0"/>' % ( + path_data, rgb2hex(rgbFace[:3]), rgb2hex(gc.get_rgb()[:3])) + self._svgwriter.write(path) + self._svgwriter.write('\n </pattern>\n</defs>') + self._hatchd[dictkey] = id + return id + def _get_style(self, gc, rgbFace): """ return the style string. style is generated from the GraphicsContext, rgbFace and clippath """ - if rgbFace is None: - fill = 'none' + if gc.get_hatch() is not None: + fill = "url(#%s)" % self._get_hatch(gc, rgbFace) else: - fill = rgb2hex(rgbFace[:3]) + if rgbFace is None: + fill = 'none' + else: + fill = rgb2hex(rgbFace[:3]) offset, seq = gc.get_dashes() if seq is None: @@ -150,7 +174,7 @@ def open_group(self, s, gid=None): """ Open a grouping element with label *s*. If *gid* is given, use - *gid* as the id of the group. + *gid* as the id of the group. """ if gid: self._svgwriter.write('<g id="%s">\n' % (gid)) Modified: trunk/matplotlib/lib/matplotlib/path.py =================================================================== --- trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 14:08:13 UTC (rev 6708) @@ -11,7 +11,7 @@ from matplotlib._path import point_in_path, get_path_extents, \ point_in_path_collection, get_path_collection_extents, \ path_in_path, path_intersects_path, convert_path_to_polygons -from matplotlib.cbook import simple_linear_interpolation +from matplotlib.cbook import simple_linear_interpolation, maxdict class Path(object): """ @@ -115,8 +115,8 @@ self.codes = codes self.vertices = vertices - #@staticmethod - def make_compound_path(*args): + #@classmethod + def make_compound_path(cls, *args): """ (staticmethod) Make a compound path from a list of Path objects. Only polygons (not curves) are supported. @@ -130,14 +130,14 @@ vertices = np.vstack([x.vertices for x in args]) vertices.reshape((total_length, 2)) - codes = Path.LINETO * np.ones(total_length) + codes = cls.LINETO * np.ones(total_length) i = 0 for length in lengths: - codes[i] = Path.MOVETO + codes[i] = cls.MOVETO i += length - return Path(vertices, codes) - make_compound_path = staticmethod(make_compound_path) + return cls(vertices, codes) + make_compound_path = classmethod(make_compound_path) def __repr__(self): return "Path(%s, %s)" % (self.vertices, self.codes) @@ -343,7 +343,7 @@ """ 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], [0.0, 0.0]]) + cls([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]) return cls._unit_rectangle unit_rectangle = classmethod(unit_rectangle) @@ -366,7 +366,7 @@ # "points-up" theta += np.pi / 2.0 verts = np.concatenate((np.cos(theta), np.sin(theta)), 1) - path = Path(verts) + path = cls(verts) cls._unit_regular_polygons[numVertices] = path return path unit_regular_polygon = classmethod(unit_regular_polygon) @@ -392,7 +392,7 @@ r = np.ones(ns2 + 1) r[1::2] = innerCircle verts = np.vstack((r*np.cos(theta), r*np.sin(theta))).transpose() - path = Path(verts) + path = cls(verts) cls._unit_regular_polygons[(numVertices, innerCircle)] = path return path unit_regular_star = classmethod(unit_regular_star) @@ -466,7 +466,7 @@ codes[0] = cls.MOVETO codes[-1] = cls.CLOSEPOLY - cls._unit_circle = Path(vertices, codes) + cls._unit_circle = cls(vertices, codes) return cls._unit_circle unit_circle = classmethod(unit_circle) @@ -523,19 +523,19 @@ if is_wedge: length = n * 3 + 4 - vertices = np.zeros((length, 2), np.float_) - codes = Path.CURVE4 * np.ones((length, ), Path.code_type) + vertices = np.empty((length, 2), np.float_) + codes = cls.CURVE4 * np.ones((length, ), cls.code_type) vertices[1] = [xA[0], yA[0]] - codes[0:2] = [Path.MOVETO, Path.LINETO] - codes[-2:] = [Path.LINETO, Path.CLOSEPOLY] + codes[0:2] = [cls.MOVETO, cls.LINETO] + codes[-2:] = [cls.LINETO, cls.CLOSEPOLY] vertex_offset = 2 end = length - 2 else: length = n * 3 + 1 - vertices = np.zeros((length, 2), np.float_) - codes = Path.CURVE4 * np.ones((length, ), Path.code_type) + vertices = np.empty((length, 2), np.float_) + codes = cls.CURVE4 * np.ones((length, ), cls.code_type) vertices[0] = [xA[0], yA[0]] - codes[0] = Path.MOVETO + codes[0] = cls.MOVETO vertex_offset = 1 end = length @@ -546,7 +546,7 @@ vertices[vertex_offset+2:end:3, 0] = xB vertices[vertex_offset+2:end:3, 1] = yB - return Path(vertices, codes) + return cls(vertices, codes) arc = classmethod(arc) #@classmethod @@ -562,6 +562,94 @@ return cls.arc(theta1, theta2, n, True) wedge = classmethod(wedge) + _hatch_dict = maxdict(8) + #@classmethod + def hatch(cls, hatchpattern, density=6): + """ + Given a hatch specifier, *hatchpattern*, generates a Path that + can be used in a repeated hatching pattern. *density* is the + number of lines per unit square. + """ + if hatchpattern is None: + return None + + hatch = hatchpattern.lower() + hatch_path = cls._hatch_dict.get((hatch, density)) + if hatch_path is not None: + return hatch_path + + size = 1.0 + density = int(density) + counts = [ + hatch.count('-') + hatch.count('+'), + hatch.count('/') + hatch.count('x'), + hatch.count('|') + hatch.count('+'), + hatch.count('\\') + hatch.count('x') + ] + + if sum(counts) == 0: + return cls([]) + + counts = [x * density for x in counts] + + num_vertices = (counts[0] * 2 + counts[1] * 4 + + counts[2] * 2 + counts[3] * 4) + vertices = np.empty((num_vertices, 2)) + codes = np.empty((num_vertices,), cls.code_type) + codes[0::2] = cls.MOVETO + codes[1::2] = cls.LINETO + + cursor = 0 + + if counts[0]: + vertices_chunk = vertices[cursor:cursor + counts[0] * 2] + cursor += counts[0] * 2 + steps = np.linspace(0.0, 1.0, counts[0], False) + vertices_chunk[0::2, 0] = 0.0 + vertices_chunk[0::2, 1] = steps + vertices_chunk[1::2, 0] = size + vertices_chunk[1::2, 1] = steps + + if counts[1]: + vertices_chunk = vertices[cursor:cursor + counts[1] * 4] + cursor += counts[1] * 4 + steps = np.linspace(0.0, 1.0, counts[1], False) + vertices_chunk[0::4, 0] = 0.0 + vertices_chunk[0::4, 1] = steps + vertices_chunk[1::4, 0] = size - steps + vertices_chunk[1::4, 1] = size + vertices_chunk[2::4, 0] = size - steps + vertices_chunk[2::4, 1] = 0.0 + vertices_chunk[3::4, 0] = size + vertices_chunk[3::4, 1] = steps + + if counts[2]: + vertices_chunk = vertices[cursor:cursor + counts[2] * 2] + cursor += counts[2] * 2 + steps = np.linspace(0.0, 1.0, counts[2], False) + vertices_chunk[0::2, 0] = steps + vertices_chunk[0::2, 1] = 0.0 + vertices_chunk[1::2, 0] = steps + vertices_chunk[1::2, 1] = size + + if counts[3]: + vertices_chunk = vertices[cursor:cursor + counts[3] * 4] + cursor += counts[3] * 4 + steps = np.linspace(0.0, 1.0, counts[3], False) + vertices_chunk[0::4, 0] = size + vertices_chunk[0::4, 1] = steps + vertices_chunk[1::4, 0] = steps + vertices_chunk[1::4, 1] = size + vertices_chunk[2::4, 0] = steps + vertices_chunk[2::4, 1] = 0.0 + vertices_chunk[3::4, 0] = 0.0 + vertices_chunk[3::4, 1] = steps + + hatch_path = cls(vertices, codes) + cls._hatch_dict[(hatch, density)] = hatch_path + return hatch_path + hatch = classmethod(hatch) + _get_path_collection_extents = get_path_collection_extents def get_path_collection_extents(*args): """ Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-12-29 14:08:13 UTC (rev 6708) @@ -30,6 +30,7 @@ #include "agg_span_image_filter_gray.h" #include "agg_span_image_filter_rgba.h" #include "agg_span_interpolator_linear.h" +#include "agg_span_pattern_rgba.h" #include "agg_conv_shorten_path.h" #include "util/agg_color_conv_rgb8.h" @@ -149,6 +150,7 @@ _set_clip_rectangle(gc); _set_clip_path(gc); _set_snap(gc); + _set_hatch_path(gc); } GCAgg::GCAgg(double dpi) : @@ -273,6 +275,15 @@ } } +void +GCAgg::_set_hatch_path( const Py::Object& gc) { + _VERBOSE("GCAgg::_set_hatch_path"); + + Py::Object method_obj = gc.getAttr("get_hatch_path"); + Py::Callable method(method_obj); + hatchpath = method.apply(Py::Tuple()); +} + const size_t RendererAgg::PIXELS_PER_INCH(96); @@ -310,6 +321,7 @@ rendererBase.clear(agg::rgba(1, 1, 1, 0)); rendererAA.attach(rendererBase); rendererBin.attach(rendererBase); + hatchRenderingBuffer.attach(hatchBuffer, HATCH_SIZE, HATCH_SIZE, HATCH_SIZE*4); } void RendererAgg::create_alpha_buffers() { @@ -879,6 +891,55 @@ } } + // Render hatch + if (!gc.hatchpath.isNone()) { + // Reset any clipping that may be in effect, since we'll be + // drawing the hatch in a scratch buffer at origin (0, 0) + theRasterizer.reset_clipping(); + rendererBase.reset_clipping(true); + + // Create and transform the path + typedef agg::conv_transform<PathIterator> hatch_path_trans_t; + typedef SimplifyPath<hatch_path_trans_t> hatch_path_simplify_t; + typedef agg::conv_stroke<hatch_path_simplify_t> hatch_path_stroke_t; + + PathIterator hatch_path(gc.hatchpath); + agg::trans_affine hatch_trans; + hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE); + hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans); + hatch_path_simplify_t hatch_path_simplify + (hatch_path_trans, true, false, HATCH_SIZE, HATCH_SIZE); + hatch_path_stroke_t hatch_path_stroke(hatch_path_simplify); + hatch_path_stroke.width(1.0); + hatch_path_stroke.line_cap(agg::square_cap); + theRasterizer.add_path(hatch_path_stroke); + + // Render the path into the hatch buffer + pixfmt hatch_img_pixf(hatchRenderingBuffer); + renderer_base rb(hatch_img_pixf); + renderer_aa rs(rb); + rb.clear(agg::rgba(0.0, 0.0, 0.0, 0.0)); + rs.color(gc.color); + agg::render_scanlines(theRasterizer, slineP8, rs); + + // Put clipping back on, if originally set on entry to this + // function + set_clipbox(gc.cliprect, theRasterizer); + if (has_clippath) + render_clippath(gc.clippath, gc.clippath_trans); + + // Transfer the hatch to the main image buffer + typedef agg::image_accessor_wrap<pixfmt, + agg::wrap_mode_repeat_auto_pow2, + agg::wrap_mode_repeat_auto_pow2> img_source_type; + typedef agg::span_pattern_rgba<img_source_type> span_gen_type; + agg::span_allocator<agg::rgba8> sa; + img_source_type img_src(hatch_img_pixf); + span_gen_type sg(img_src, 0, 0); + theRasterizer.add_path(path); + agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg); + } + // Render stroke if (gc.linewidth != 0.0) { double linewidth = gc.linewidth; Modified: trunk/matplotlib/src/_backend_agg.h =================================================================== --- trunk/matplotlib/src/_backend_agg.h 2008-12-29 13:58:18 UTC (rev 6707) +++ trunk/matplotlib/src/_backend_agg.h 2008-12-29 14:08:13 UTC (rev 6708) @@ -60,7 +60,6 @@ typedef agg::scanline_bin scanline_bin; typedef agg::amask_no_clip_gray8 alpha_mask_type; - typedef agg::renderer_base<agg::pixfmt_gray8> renderer_base_alpha_mask_type; typedef agg::renderer_scanline_aa_solid<renderer_base_alpha_mask_type> renderer_alpha_mask_type; @@ -129,6 +128,8 @@ SNAP_TRUE } snap; + Py::Object hatchpath; + protected: agg::rgba get_color(const Py::Object& gc); double points_to_pixels( const Py::Object& points); @@ -139,6 +140,7 @@ void _set_clip_path( const Py::Object& gc); void _set_antialiased( const Py::Object& gc); void _set_snap( const Py::Object& gc); + void _set_hatch_path( const Py::Object& gc); }; @@ -206,6 +208,12 @@ Py::Object lastclippath; agg::trans_affine lastclippath_transform; + // HATCH_SIZE should be a power of 2, to take advantage of Agg's + // fast pattern rendering + static const size_t HATCH_SIZE = 128; + agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4]; + agg::rendering_buffer hatchRenderingBuffer; + const int debug; protected: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-29 14:23:28
|
Revision: 6710 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6710&view=rev Author: mdboom Date: 2008-12-29 14:23:24 +0000 (Mon, 29 Dec 2008) Log Message: ----------- Update hatch documentation. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/hatch_demo.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-29 14:10:22 UTC (rev 6709) +++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2008-12-29 14:23:24 UTC (rev 6710) @@ -1,8 +1,6 @@ """ -Hatching (pattern filled polygons) is supported currently on PS and PDF -backend only. See the set_patch method in -http://matplotlib.sf.net/matplotlib.patches.html#Patch -for details +Hatching (pattern filled polygons) is supported currently in the PS, +PDF, SVG and Agg backends only. """ import matplotlib.pyplot as plt Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-12-29 14:10:22 UTC (rev 6709) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-12-29 14:23:24 UTC (rev 6710) @@ -228,11 +228,11 @@ 'return whether fill is set' return self.fill - def set_hatch(self, h): + def set_hatch(self, hatch): """ Set the hatching pattern - hatch can be one of:: + *hatch* can be one of:: / - diagonal hatching \ - back diagonal @@ -247,11 +247,9 @@ CURRENT LIMITATIONS: - 1. Hatching is supported in the PostScript and the PDF backend only. + 1. Hatching is supported in the PostScript, PDF, SVG and Agg + backends only. - 2. Hatching is done with solid black lines of width 0. - - ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] (ps & pdf backend only) """ self._hatch = h @@ -2655,7 +2653,7 @@ """ path = make_path_regular(path) - + if aspect_ratio is not None: # Squeeze the given height by the aspect_ratio @@ -2808,27 +2806,27 @@ [(x3+ddxB, y3+ddyB)]]), path.codes)] _fillable = [False] - + if self.beginarrow: if self.fillbegin: p = np.concatenate([verticesA, [verticesA[0], verticesA[0]], ]) - c = np.concatenate([codesA, [Path.LINETO, Path.CLOSEPOLY]]) + c = np.concatenate([codesA, [Path.LINETO, Path.CLOSEPOLY]]) _path.append(Path(p, c)) _fillable.append(True) else: _path.append(Path(verticesA, codesA)) _fillable.append(False) - + if self.endarrow: if self.fillend: _fillable.append(True) p = np.concatenate([verticesB, [verticesB[0], verticesB[0]], ]) - c = np.concatenate([codesB, [Path.LINETO, Path.CLOSEPOLY]]) + c = np.concatenate([codesB, [Path.LINETO, Path.CLOSEPOLY]]) _path.append(Path(p, c)) else: _fillable.append(False) _path.append(Path(verticesB, codesB)) - + return _path, _fillable @@ -2926,7 +2924,7 @@ super(ArrowStyle.CurveFilledA, self).__init__( \ beginarrow=True, endarrow=False, - fillbegin=True, fillend=False, + fillbegin=True, fillend=False, head_length=head_length, head_width=head_width ) _style_list["<|-"] = CurveFilledA @@ -2948,7 +2946,7 @@ super(ArrowStyle.CurveFilledB, self).__init__( \ beginarrow=False, endarrow=True, - fillbegin=False, fillend=True, + fillbegin=False, fillend=True, head_length=head_length, head_width=head_width ) _style_list["-|>"] = CurveFilledB @@ -2970,7 +2968,7 @@ super(ArrowStyle.CurveFilledAB, self).__init__( \ beginarrow=True, endarrow=True, - fillbegin=True, fillend=True, + fillbegin=True, fillend=True, head_length=head_length, head_width=head_width ) _style_list["<|-|>"] = CurveFilledAB @@ -3532,7 +3530,7 @@ if cbook.iterable(fillable): _path = concatenate_paths(_path) - + return self.get_transform().inverted().transform_path(_path) @@ -3604,8 +3602,8 @@ if not cbook.iterable(fillable): path = [path] fillable = [fillable] - + affine = transforms.IdentityTransform() renderer.open_group('patch', self.get_gid()) @@ -3615,6 +3613,6 @@ renderer.draw_path(gc, p, affine, rgbFace) else: renderer.draw_path(gc, p, affine, None) - + renderer.close_group('patch') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-29 14:42:23
|
Revision: 6712 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6712&view=rev Author: mdboom Date: 2008-12-29 14:42:20 +0000 (Mon, 29 Dec 2008) Log Message: ----------- Fix path simplification by a) making it more conservative about when it will simplify based on segment length, and b) honoring path.simplify rcParam in Agg backend. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/config/rcsetup.py trunk/matplotlib/lib/matplotlib/path.py trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/src/agg_py_path_iterator.h Modified: trunk/matplotlib/lib/matplotlib/config/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2008-12-29 14:25:47 UTC (rev 6711) +++ trunk/matplotlib/lib/matplotlib/config/rcsetup.py 2008-12-29 14:42:20 UTC (rev 6712) @@ -479,7 +479,7 @@ 'svg.embed_char_paths' : [True, validate_bool], # True to save all characters as paths in the SVG 'plugins.directory' : ['.matplotlib_plugins', str], # where plugin directory is locate - 'path.simplify' : [False, validate_bool] + 'path.simplify' : [True, validate_bool] } if __name__ == '__main__': Modified: trunk/matplotlib/lib/matplotlib/path.py =================================================================== --- trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 14:25:47 UTC (rev 6711) +++ trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 14:42:20 UTC (rev 6712) @@ -109,8 +109,9 @@ assert vertices.ndim == 2 assert vertices.shape[1] == 2 - self.should_simplify = (len(vertices) >= 128 and - (codes is None or np.all(codes <= Path.LINETO))) + self.should_simplify = (rcParam['path.simplify'] and + (len(vertices) >= 128 and + (codes is None or np.all(codes <= Path.LINETO)))) self.has_nonfinite = not np.isfinite(vertices).all() self.codes = codes self.vertices = vertices Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-29 14:25:47 UTC (rev 6711) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-29 14:42:20 UTC (rev 6712) @@ -518,7 +518,7 @@ 'docstring.hardcopy' : [False, validate_bool], # set this when you want to generate hardcopy docstring 'plugins.directory' : ['.matplotlib_plugins', str], # where plugin directory is locate - 'path.simplify' : [False, validate_bool], + 'path.simplify' : [True, validate_bool], 'agg.path.chunksize' : [0, validate_int] # 0 to disable chunking; # recommend about 20000 to # enable. Experimental. Modified: trunk/matplotlib/src/agg_py_path_iterator.h =================================================================== --- trunk/matplotlib/src/agg_py_path_iterator.h 2008-12-29 14:25:47 UTC (rev 6711) +++ trunk/matplotlib/src/agg_py_path_iterator.h 2008-12-29 14:42:20 UTC (rev 6712) @@ -353,7 +353,7 @@ //if the perp vector is less than some number of (squared) //pixels in size, then merge the current vector - if (perpdNorm2 < 0.25) + if (perpdNorm2 < (1.0 / 9.0)) { //check if the current vector is parallel or //anti-parallel to the orig vector. If it is parallel, test This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-12-29 15:34:33
|
Revision: 6716 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6716&view=rev Author: mdboom Date: 2008-12-29 15:34:30 +0000 (Mon, 29 Dec 2008) Log Message: ----------- Merged revisions 6714-6715 via svnmerge from https://matplotlib.svn.sf.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6714 | mdboom | 2008-12-29 10:29:52 -0500 (Mon, 29 Dec 2008) | 2 lines Handle path.simplify rcParam in all backends. ........ r6715 | mdboom | 2008-12-29 10:33:18 -0500 (Mon, 29 Dec 2008) | 2 lines Handle path.simplify rcParam in all backends. ........ Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/path.py Property Changed: ---------------- trunk/matplotlib/ trunk/matplotlib/doc/pyplots/README trunk/matplotlib/doc/sphinxext/gen_gallery.py trunk/matplotlib/doc/sphinxext/gen_rst.py Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6689 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6715 Modified: svn:mergeinfo - /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673 + /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715 Property changes on: trunk/matplotlib/doc/pyplots/README ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673 + /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715 Property changes on: trunk/matplotlib/doc/sphinxext/gen_gallery.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673 + /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673,6714-6715 Property changes on: trunk/matplotlib/doc/sphinxext/gen_rst.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 + /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_rst.py:6714-6715 Modified: trunk/matplotlib/lib/matplotlib/path.py =================================================================== --- trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 15:33:18 UTC (rev 6715) +++ trunk/matplotlib/lib/matplotlib/path.py 2008-12-29 15:34:30 UTC (rev 6716) @@ -112,7 +112,7 @@ self.should_simplify = (rcParams['path.simplify'] and (len(vertices) >= 128 and - (codes is None or np.all(codes <= Path.LINETO)))) + (codes is None or np.all(codes <= Path.LINETO)))) self.has_nonfinite = not np.isfinite(vertices).all() self.codes = codes self.vertices = vertices This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2008-12-31 13:20:56
|
Revision: 6718 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6718&view=rev Author: jouni Date: 2008-12-31 13:20:50 +0000 (Wed, 31 Dec 2008) Log Message: ----------- Improve pdf usetex by adding support for font effects Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/api/index_backend_api.rst trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/dviread.py trunk/matplotlib/lib/matplotlib/type1font.py Added Paths: ----------- trunk/matplotlib/doc/api/dviread.rst trunk/matplotlib/doc/api/type1font.rst trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-30 16:06:59 UTC (rev 6717) +++ trunk/matplotlib/CHANGELOG 2008-12-31 13:20:50 UTC (rev 6718) @@ -1,3 +1,6 @@ +2008-12-31 Improve pdf usetex by adding support for font effects + (slanting and extending). - JKS + 2008-12-29 Fix a bug in pdf usetex support, which occurred if the same Type-1 font was used with different encodings, e.g. with Minion Pro and MnSymbol. - JKS Added: trunk/matplotlib/doc/api/dviread.rst =================================================================== --- trunk/matplotlib/doc/api/dviread.rst (rev 0) +++ trunk/matplotlib/doc/api/dviread.rst 2008-12-31 13:20:50 UTC (rev 6718) @@ -0,0 +1,8 @@ + +:mod:`matplotlib.dviread` +========================= + +.. automodule:: matplotlib.dviread + :members: + :undoc-members: + :show-inheritance: Modified: trunk/matplotlib/doc/api/index_backend_api.rst =================================================================== --- trunk/matplotlib/doc/api/index_backend_api.rst 2008-12-30 16:06:59 UTC (rev 6717) +++ trunk/matplotlib/doc/api/index_backend_api.rst 2008-12-31 13:20:50 UTC (rev 6718) @@ -8,3 +8,5 @@ backend_gtkagg_api.rst backend_qt4agg_api.rst backend_wxagg_api.rst + dviread.rst + type1font.rst Added: trunk/matplotlib/doc/api/type1font.rst =================================================================== --- trunk/matplotlib/doc/api/type1font.rst (rev 0) +++ trunk/matplotlib/doc/api/type1font.rst 2008-12-31 13:20:50 UTC (rev 6718) @@ -0,0 +1,8 @@ + +:mod:`matplotlib.type1font` +=========================== + +.. automodule:: matplotlib.type1font + :members: + :undoc-members: + :show-inheritance: Added: trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/usetex_fonteffects.py 2008-12-31 13:20:50 UTC (rev 6718) @@ -0,0 +1,22 @@ +# This script demonstrates that font effects specified in your pdftex.map +# are now supported in pdf usetex. + +import matplotlib +matplotlib.rc('text', usetex=True) +import pylab + +def setfont(font): + return r'\font\a %s at 14pt\a ' % font + +for y, font, text in zip(range(5), + ['ptmr8r', 'ptmri8r', 'ptmro8r', 'ptmr8rn', 'ptmrr8re'], + ['Nimbus Roman No9 L ' + x for x in + ['', 'Italics (real italics for comparison)', + '(slanted)', '(condensed)', '(extended)']]): + pylab.text(0, y, setfont(font) + text) + +pylab.ylim(-1, 5) +pylab.xlim(-0.2, 0.6) +pylab.setp(pylab.gca(), frame_on=False, xticks=(), yticks=()) +pylab.title('Usetex font effects') +pylab.savefig('usetex_fonteffects.pdf') Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-30 16:06:59 UTC (rev 6717) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-31 13:20:50 UTC (rev 6718) @@ -500,7 +500,7 @@ # from pdf.use14corefonts fontdictObject = self._write_afm_font(filename) elif self.dviFontInfo.has_key(filename): - # a Type 1 font from a dvi file + # a Type 1 font from a dvi file; the filename is really the TeX name fontdictObject = self.embedType1(filename, self.dviFontInfo[filename]) else: # a normal TrueType font @@ -525,22 +525,25 @@ return fontdictObject def embedType1(self, texname, fontinfo): - # TODO: font effects such as SlantFont matplotlib.verbose.report( - 'Embedding Type 1 font ' + fontinfo.fontfile + - ' with encoding ' + (fontinfo.encodingfile or '(none)'), + 'Embedding ' + texname + + ' which is the Type 1 font ' + fontinfo.fontfile + + ' with encoding ' + (fontinfo.encodingfile or '(none)') + + ' and effects ' + `fontinfo.effects`, 'debug') - # Use FT2Font to get several font properties - font = FT2Font(fontinfo.fontfile) + t1font = type1font.Type1Font(fontinfo.fontfile) + if fontinfo.effects: + t1font = t1font.transform(fontinfo.effects) # Font descriptors may be shared between differently encoded # Type-1 fonts, so only create a new descriptor if there is no # existing descriptor for this font. - fontdesc = self.type1Descriptors.get(fontinfo.fontfile) + effects = (fontinfo.effects.get('slant', 0.0), fontinfo.effects.get('extend', 1.0)) + fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects)) if fontdesc is None: - fontdesc = self.createType1Descriptor(font, fontinfo.fontfile) - self.type1Descriptors[fontinfo.fontfile] = fontdesc + fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile) + self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc # Widths widthsObject = self.reserveObject('font widths') @@ -551,7 +554,7 @@ fontdict = { 'Type': Name('Font'), 'Subtype': Name('Type1'), - 'BaseFont': Name(font.postscript_name), + 'BaseFont': Name(t1font.prop['FontName']), 'FirstChar': 0, 'LastChar': len(fontinfo.widths) - 1, 'Widths': widthsObject, @@ -571,14 +574,14 @@ self.writeObject(fontdictObject, fontdict) return fontdictObject - def createType1Descriptor(self, font, fontfile): + def createType1Descriptor(self, t1font, fontfile): # Create and write the font descriptor and the font file # of a Type-1 font fontdescObject = self.reserveObject('font descriptor') fontfileObject = self.reserveObject('font file') - _, _, fullname, familyname, weight, italic_angle, fixed_pitch, \ - ul_position, ul_thickness = font.get_ps_font_info() + italic_angle = t1font.prop['ItalicAngle'] + fixed_pitch = t1font.prop['isFixedPitch'] flags = 0 if fixed_pitch: flags |= 1 << 0 # fixed width @@ -590,18 +593,20 @@ if 0: flags |= 1 << 17 # TODO: small caps if 0: flags |= 1 << 18 # TODO: force bold + ft2font = FT2Font(fontfile) + descriptor = { 'Type': Name('FontDescriptor'), - 'FontName': Name(font.postscript_name), + 'FontName': Name(t1font.prop['FontName']), 'Flags': flags, - 'FontBBox': font.bbox, + 'FontBBox': ft2font.bbox, 'ItalicAngle': italic_angle, - 'Ascent': font.ascender, - 'Descent': font.descender, + 'Ascent': ft2font.ascender, + 'Descent': ft2font.descender, 'CapHeight': 1000, # TODO: find this out 'XHeight': 500, # TODO: this one too 'FontFile': fontfileObject, - 'FontFamily': familyname, + 'FontFamily': t1font.prop['FamilyName'], 'StemV': 50, # TODO # (see also revision 3874; but not all TeX distros have AFM files!) #'FontWeight': a number where 400 = Regular, 700 = Bold @@ -609,7 +614,6 @@ self.writeObject(fontdescObject, descriptor) - t1font = type1font.Type1Font(fontfile) self.beginStream(fontfileObject.id, None, { 'Length1': len(t1font.parts[0]), 'Length2': len(t1font.parts[1]), @@ -1369,14 +1373,14 @@ self.file.dviFontInfo[dvifont.texname] = Bunch( fontfile=psfont.filename, encodingfile=psfont.encoding, + effects=psfont.effects, widths=dvifont.widths, dvifont=dvifont) - # TODO: font effects seq += [['font', pdfname, dvifont.size]] oldfont = dvifont seq += [['text', x1, y1, [chr(glyph)], x1+width]] - # Find consecutive text strings with constant x coordinate and + # Find consecutive text strings with constant y coordinate and # combine into a sequence of strings and kerns, or just one # string (if any kerns would be less than 0.1 points). i, curx = 0, 0 Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2008-12-30 16:06:59 UTC (rev 6717) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2008-12-31 13:20:50 UTC (rev 6718) @@ -1,12 +1,14 @@ """ An experimental module for reading dvi files output by TeX. Several limitations make this not (currently) useful as a general-purpose dvi -preprocessor. +preprocessor, but it is currently used by the pdf backend for +processing usetex text. Interface:: dvi = Dvi(filename, 72) - for page in dvi: # iterate over pages + # iterate over pages (but only one page is supported for now): + for page in dvi: w, h, d = page.width, page.height, page.descent for x,y,font,glyph,width in page.text: fontname = font.texname @@ -49,7 +51,7 @@ """ Iterate through the pages of the file. - Returns (text, pages) pairs, where: + Returns (text, boxes) pairs, where: text is a list of (x, y, fontnum, glyphnum, width) tuples boxes is a list of (x, y, height, width) tuples @@ -131,8 +133,8 @@ def _arg(self, nbytes, signed=False): """ - Read and return an integer argument "nbytes" long. - Signedness is determined by the "signed" keyword. + Read and return an integer argument *nbytes* long. + Signedness is determined by the *signed* keyword. """ str = self.file.read(nbytes) value = ord(str[0]) @@ -144,7 +146,7 @@ def _dispatch(self, byte): """ - Based on the opcode "byte", read the correct kinds of + Based on the opcode *byte*, read the correct kinds of arguments from the dvi file and call the method implementing that opcode with those arguments. """ @@ -385,9 +387,27 @@ Object that holds a font's texname and size, supports comparison, and knows the widths of glyphs in the same units as the AFM file. There are also internal attributes (for use by dviread.py) that - are _not_ used for comparison. + are *not* used for comparison. The size is in Adobe points (converted from TeX points). + + .. attribute:: texname + + Name of the font as used internally by TeX and friends. This + is usually very different from any external font names, and + :class:`dviread.PsfontsMap` can be used to find the external + name of the font. + + .. attribute:: size + + Size of the font in Adobe points, converted from the slightly + smaller TeX points. + + .. attribute:: widths + + Widths of glyphs in glyph-space units, typically 1/1000ths of + the point size. + """ __slots__ = ('texname', 'size', 'widths', '_scale', '_vf', '_tfm') @@ -532,17 +552,27 @@ A TeX Font Metric file. This implementation covers only the bare minimum needed by the Dvi class. - Attributes: + .. attribute:: checksum - checksum: for verifying against dvi file + Used for verifying against the dvi file. - design_size: design size of the font (in what units?) + .. attribute:: design_size - width[i]: width of character \#i, needs to be scaled - by the factor specified in the dvi file - (this is a dict because indexing may not start from 0) + Design size of the font (in what units?) - height[i], depth[i]: height and depth of character \#i + .. attribute:: width + + Width of each character, needs to be scaled by the factor + specified in the dvi file. This is a dict because indexing may + not start from 0. + + .. attribute:: height + + Height of each character. + + .. attribute:: depth + + Depth of each character. """ __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth') @@ -581,8 +611,20 @@ class PsfontsMap(object): """ A psfonts.map formatted file, mapping TeX fonts to PS fonts. - Usage: map = PsfontsMap('.../psfonts.map'); map['cmr10'] + Usage:: + >>> map = PsfontsMap(find_tex_file('pdftex.map')) + >>> entry = map['ptmbo8r'] + >>> entry.texname + 'ptmbo8r' + >>> entry.psname + 'Times-Bold' + >>> entry.encoding + '/usr/local/texlive/2008/texmf-dist/fonts/enc/dvips/base/8r.enc' + >>> entry.effects + {'slant': 0.16700000000000001} + >>> entry.filename + For historical reasons, TeX knows many Type-1 fonts by different names than the outside world. (For one thing, the names have to fit in eight characters.) Also, TeX's native fonts are not Type-1 @@ -594,11 +636,12 @@ file names. A texmf tree typically includes mapping files called e.g. - psfonts.map, pdftex.map, dvipdfm.map. psfonts.map is used by + psfonts.map, pdftex.map, dvipdfm.map. psfonts.map is used by dvips, pdftex.map by pdfTeX, and dvipdfm.map by dvipdfm. - psfonts.map might avoid embedding the 35 PostScript fonts, while - the pdf-related files perhaps only avoid the "Base 14" pdf fonts. - But the user may have configured these files differently. + psfonts.map might avoid embedding the 35 PostScript fonts (i.e., + have no filename for them, as in the Times-Bold example above), + while the pdf-related files perhaps only avoid the "Base 14" pdf + fonts. But the user may have configured these files differently. """ __slots__ = ('_font',) @@ -655,10 +698,10 @@ subsetting, but I have no example of << in my TeX installation. """ texname, psname = words[:2] - effects, encoding, filename = [], None, None + effects, encoding, filename = '', None, None for word in words[2:]: if not word.startswith('<'): - effects.append(word) + effects = word else: word = word.lstrip('<') if word.startswith('['): @@ -670,6 +713,18 @@ else: assert filename is None filename = word + + eff = effects.split() + effects = {} + try: + effects['slant'] = float(eff[eff.index('SlantFont')-1]) + except ValueError: + pass + try: + effects['extend'] = float(eff[eff.index('ExtendFont')-1]) + except ValueError: + pass + self._font[texname] = mpl_cbook.Bunch( texname=texname, psname=psname, effects=effects, encoding=encoding, filename=filename) @@ -733,13 +788,18 @@ def find_tex_file(filename, format=None): """ - Call kpsewhich to find a file in the texmf tree. - If format is not None, it is used as the value for the --format option. - See the kpathsea documentation for more information. + Call :program:`kpsewhich` to find a file in the texmf tree. If + *format* is not None, it is used as the value for the + :option:`--format` option. Apparently most existing TeX distributions on Unix-like systems use kpathsea. I hear MikTeX (a popular distribution on Windows) doesn't use kpathsea, so what do we do? (TODO) + + .. seealso:: + + `Kpathsea documentation <http://www.tug.org/kpathsea/>`_ + The library that :program:`kpsewhich` is part of. """ cmd = ['kpsewhich'] Modified: trunk/matplotlib/lib/matplotlib/type1font.py =================================================================== --- trunk/matplotlib/lib/matplotlib/type1font.py 2008-12-30 16:06:59 UTC (rev 6717) +++ trunk/matplotlib/lib/matplotlib/type1font.py 2008-12-31 13:20:50 UTC (rev 6718) @@ -1,37 +1,70 @@ """ -A class representing a Type 1 font. +This module contains a class representing a Type 1 font. -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. +This version reads pfa and pfb files and splits them for embedding in +pdf files. It also supports SlantFont and ExtendFont transformations, +similarly to pdfTeX and friends. There is no support yet for +subsetting. -Usage (subject to change): +Usage:: - font = Type1Font(filename) - clear_part, encrypted_part, finale = font.parts + >>> font = Type1Font(filename) + >>> clear_part, encrypted_part, finale = font.parts + >>> slanted_font = font.transform({'slant': 0.167}) + >>> extended_font = font.transform({'extend': 1.2}) -Source: Adobe Technical Note #5040, Supporting Downloadable PostScript -Language Fonts. +Sources: -If extending this class, see also: Adobe Type 1 Font Format, Adobe -Systems Incorporated, third printing, v1.1, 1993. ISBN 0-201-57044-0. +* Adobe Technical Note #5040, Supporting Downloadable PostScript + Language Fonts. + +* Adobe Type 1 Font Format, Adobe Systems Incorporated, third printing, + v1.1, 1993. ISBN 0-201-57044-0. """ +import matplotlib.cbook as cbook +import cStringIO +import itertools +import numpy as np import re import struct class Type1Font(object): + """ + A class representing a Type-1 font, for use by backends. - def __init__(self, filename): - file = open(filename, 'rb') - try: - data = self._read(file) - finally: - file.close() - self.parts = self._split(data) - #self._parse() + .. attribute:: parts + A 3-tuple of the cleartext part, the encrypted part, and the + finale of zeros. + + .. attribute:: prop + + A dictionary of font properties. + """ + __slots__ = ('parts', 'prop') + + def __init__(self, input): + """ + Initialize a Type-1 font. *input* can be either the file name of + a pfb file or a 3-tuple of already-decoded Type-1 font parts. + """ + if isinstance(input, tuple) and len(input) == 3: + self.parts = input + else: + file = open(input, 'rb') + try: + data = self._read(file) + finally: + file.close() + self.parts = self._split(data) + + self._parse() + def _read(self, file): + """ + Read the font from a file, decoding into usable parts. + """ rawdata = file.read() if not rawdata.startswith(chr(128)): return rawdata @@ -100,85 +133,177 @@ return data[:len1], binary, data[idx:] _whitespace = re.compile(r'[\0\t\r\014\n ]+') - _delim = re.compile(r'[()<>[]{}/%]') _token = re.compile(r'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+') _comment = re.compile(r'%[^\r\n\v]*') _instring = re.compile(r'[()\\]') - def _parse(self): + @classmethod + def _tokens(cls, text): """ - A very limited kind of parsing to find the Encoding of the - font. + A PostScript tokenizer. Yield (token, value) pairs such as + ('whitespace', ' ') or ('name', '/Foobar'). """ - def tokens(text): - """ - Yield pairs (position, token), ignoring comments and - whitespace. Numbers count as tokens. - """ - pos = 0 - while pos < len(text): - match = self._comment.match(text[pos:]) or self._whitespace.match(text[pos:]) + pos = 0 + while pos < len(text): + match = cls._comment.match(text[pos:]) or cls._whitespace.match(text[pos:]) + if match: + yield ('whitespace', match.group()) + pos += match.end() + elif text[pos] == '(': + start = pos + pos += 1 + depth = 1 + while depth: + match = cls._instring.search(text[pos:]) + if match is None: return + pos += match.end() + if match.group() == '(': + depth += 1 + elif match.group() == ')': + depth -= 1 + else: # a backslash - skip the next character + pos += 1 + yield ('string', text[start:pos]) + elif text[pos:pos+2] in ('<<', '>>'): + yield ('delimiter', text[pos:pos+2]) + pos += 2 + elif text[pos] == '<': + start = pos + pos += text[pos:].index('>') + yield ('string', text[start:pos]) + else: + match = cls._token.match(text[pos:]) if match: + try: + float(match.group()) + yield ('number', match.group()) + except ValueError: + yield ('name', match.group()) pos += match.end() - elif text[pos] == '(': - start = pos + else: + yield ('delimiter', text[pos]) pos += 1 - depth = 1 - while depth: - match = self._instring.search(text[pos:]) - if match is None: return - if match.group() == '(': - depth += 1 - pos += 1 - elif match.group() == ')': - depth -= 1 - pos += 1 - else: - pos += 2 - yield (start, text[start:pos]) - elif text[pos:pos+2] in ('<<', '>>'): - yield (pos, text[pos:pos+2]) - pos += 2 - elif text[pos] == '<': - start = pos - pos += text[pos:].index('>') - yield (start, text[start:pos]) - else: - match = self._token.match(text[pos:]) - if match: - yield (pos, match.group()) - pos += match.end() + + def _parse(self): + """ + Find the values of various font properties. This limited kind + of parsing is described in Chapter 10 "Adobe Type Manager + Compatibility" of the Type-1 spec. + """ + # Start with reasonable defaults + prop = { 'weight': 'Regular', 'ItalicAngle': 0.0, 'isFixedPitch': False, + 'UnderlinePosition': -100, 'UnderlineThickness': 50 } + tokenizer = self._tokens(self.parts[0]) + filtered = itertools.ifilter(lambda x: x[0] != 'whitespace', tokenizer) + for token, value in filtered: + if token == 'name' and value.startswith('/'): + key = value[1:] + token, value = filtered.next() + if token == 'name': + if value in ('true', 'false'): + value = value == 'true' else: - yield (pos, text[pos]) - pos += 1 + value = value.lstrip('/') + elif token == 'string': + value = value.lstrip('(').rstrip(')') + elif token == 'number': + if '.' in value: value = float(value) + else: value = int(value) + else: # more complicated value such as an array + value = None + if key != 'FontInfo' and value is not None: + prop[key] = value - enc_starts, enc_ends = None, None - state = 0 - # State transitions: - # 0 -> /Encoding -> 1 - # 1 -> StandardEncoding -> 2 -> def -> (ends) - # 1 -> dup -> 4 -> put -> 5 - # 5 -> dup -> 4 -> put -> 5 - # 5 -> def -> (ends) - for pos,token in tokens(self.parts[0]): - if state == 0 and token == '/Encoding': - enc_starts = pos - state = 1 - elif state == 1 and token == 'StandardEncoding': - state = 2 - elif state in (2,5) and token == 'def': - enc_ends = pos+3 - break - elif state in (1,5) and token == 'dup': - state = 4 - elif state == 4 and token == 'put': - state = 5 - self.enc_starts, self.enc_ends = enc_starts, enc_ends + # Fill in the various *Name properties + if not prop.has_key('FontName'): + prop['FontName'] = prop.get('FullName') or prop.get('FamilyName') or 'Unknown' + if not prop.has_key('FullName'): + prop['FullName'] = prop['FontName'] + if not prop.has_key('FamilyName'): + extras = r'(?i)([ -](regular|plain|italic|oblique|(semi)?bold|(ultra)?light|extra|condensed))+$' + prop['FamilyName'] = re.sub(extras, '', prop['FullName']) + self.prop = prop + + @classmethod + def _transformer(cls, tokens, slant, extend): + def fontname(name): + result = name + if slant: result += '_Slant_' + str(int(1000*slant)) + if extend != 1.0: result += '_Extend_' + str(int(1000*extend)) + return result + + def italicangle(angle): + return str(float(angle) - np.arctan(slant)/np.pi*180) + + def fontmatrix(array): + array = array.lstrip('[').rstrip(']').strip().split() + array = [ float(x) for x in array ] + oldmatrix = np.eye(3,3) + oldmatrix[0:3,0] = array[::2] + oldmatrix[0:3,1] = array[1::2] + modifier = np.array([[extend, 0, 0], + [slant, 1, 0], + [0, 0, 1]]) + newmatrix = np.dot(modifier, oldmatrix) + array[::2] = newmatrix[0:3,0] + array[1::2] = newmatrix[0:3,1] + return '[' + ' '.join(str(x) for x in array) + ']' + + def replace(fun): + def replacer(tokens): + token, value = tokens.next() # name, e.g. /FontMatrix + yield value + token, value = tokens.next() # possible whitespace + while token == 'whitespace': + yield value + token, value = tokens.next() + if value != '[': # name/number/etc. + yield fun(value) + else: # array, e.g. [1 2 3] + array = [] + while value != ']': + array += value + token, value = tokens.next() + array += value + yield fun(''.join(array)) + return replacer + + def suppress(tokens): + for x in itertools.takewhile(lambda x: x[1] != 'def', tokens): + pass + yield '' + + table = { '/FontName': replace(fontname), + '/ItalicAngle': replace(italicangle), + '/FontMatrix': replace(fontmatrix), + '/UniqueID': suppress } + + while True: + token, value = tokens.next() + if token == 'name' and value in table: + for value in table[value](itertools.chain([(token, value)], tokens)): + yield value + else: + yield value + + def transform(self, effects): + """ + Transform the font by slanting or extending. *effects* should + be a dict where ``effects['slant']`` is the tangent of the + angle that the font is to be slanted to the right (so negative + values slant to the left) and ``effects['extend']`` is the + multiplier by which the font is to be extended (so values less + than 1.0 condense). Returns a new :class:`Type1Font` object. + """ + + buffer = cStringIO.StringIO() + tokenizer = self._tokens(self.parts[0]) + for value in self._transformer(tokenizer, + slant=effects.get('slant', 0.0), + extend=effects.get('extend', 1.0)): + buffer.write(value) + result = buffer.getvalue() + buffer.close() + + return Type1Font((result, self.parts[1], self.parts[2])) -if __name__ == '__main__': - import sys - font = Type1Font(sys.argv[1]) - parts = font.parts - print len(parts[0]), len(parts[1]), len(parts[2]) - #print parts[0][font.enc_starts:font.enc_ends] - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2009-01-01 22:24:03
|
Revision: 6730 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6730&view=rev Author: jouni Date: 2009-01-01 22:23:57 +0000 (Thu, 01 Jan 2009) Log Message: ----------- Allow multipage pdf files Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/multipage_pdf.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-31 20:19:16 UTC (rev 6729) +++ trunk/matplotlib/CHANGELOG 2009-01-01 22:23:57 UTC (rev 6730) @@ -1,3 +1,5 @@ +2009-01-02 Allow multipage pdf files. - JKS + 2008-12-31 Improve pdf usetex by adding support for font effects (slanting and extending). - JKS Added: trunk/matplotlib/examples/pylab_examples/multipage_pdf.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/multipage_pdf.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/multipage_pdf.py 2009-01-01 22:23:57 UTC (rev 6730) @@ -0,0 +1,29 @@ +import numpy as np +import matplotlib +from matplotlib.backends.backend_pdf import PdfFile +from pylab import * + +pdf = PdfFile('multipage_pdf.pdf') + +figure(figsize=(3,3)) +plot(range(7), [3,1,4,1,5,9,2], 'r-o') +title('Page One') +savefig(pdf, format='pdf') +close() + +rc('text', usetex=True) +figure(figsize=(8,6)) +x = np.arange(0,5,0.1) +plot(x, np.sin(x), 'b-') +title('Page Two') +savefig(pdf, format='pdf') +close() + +rc('text', usetex=False) +figure(figsize=(4,5)) +plot(x, x*x, 'ko') +title('Page Three') +savefig(pdf, format='pdf') +close() + +pdf.close() Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2008-12-31 20:19:16 UTC (rev 6729) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-01 22:23:57 UTC (rev 6730) @@ -219,6 +219,9 @@ def __repr__(self): return "<Name %s>" % self.name + def __str__(self): + return '/' + self.name + @staticmethod def hexify(match): return '#%02x' % ord(match.group()) @@ -336,13 +339,7 @@ class PdfFile(object): """PDF file with one page.""" - def __init__(self, width, height, dpi, filename): - self.width, self.height = width, height - self.dpi = dpi - if rcParams['path.simplify']: - self.simplify = (width * dpi, height * dpi) - else: - self.simplify = None + def __init__(self, filename): self.nextObject = 1 # next free object id self.xrefTable = [ [0, 65535, 'the zero object'] ] self.passed_in_file_object = False @@ -364,17 +361,16 @@ self.rootObject = self.reserveObject('root') self.infoObject = self.reserveObject('info') - pagesObject = self.reserveObject('pages') - thePageObject = self.reserveObject('page 0') - contentObject = self.reserveObject('contents of page 0') + self.pagesObject = self.reserveObject('pages') + self.pageList = [] self.fontObject = self.reserveObject('fonts') self.alphaStateObject = self.reserveObject('extended graphics states') self.hatchObject = self.reserveObject('tiling patterns') self.XObjectObject = self.reserveObject('external objects') - resourceObject = self.reserveObject('resources') + self.resourceObject = self.reserveObject('resources') root = { 'Type': Name('Catalog'), - 'Pages': pagesObject } + 'Pages': self.pagesObject } self.writeObject(self.rootObject, root) info = { 'Creator': 'matplotlib ' + __version__ \ @@ -385,23 +381,12 @@ # Possible TODO: Title, Author, Subject, Keywords self.writeObject(self.infoObject, info) - pages = { 'Type': Name('Pages'), - 'Kids': [ thePageObject ], - 'Count': 1 } - self.writeObject(pagesObject, pages) - - thePage = { 'Type': Name('Page'), - 'Parent': pagesObject, - 'Resources': resourceObject, - 'MediaBox': [ 0, 0, dpi*width, dpi*height ], - 'Contents': contentObject } - self.writeObject(thePageObject, thePage) - self.fontNames = {} # maps filenames to internal font names self.nextFont = 1 # next free internal font name self.dviFontInfo = {} # information on dvi fonts self.type1Descriptors = {} # differently encoded Type-1 fonts may # share the same descriptor + self.used_characters = {} self.alphaStates = {} # maps alpha values to graphics state objects self.nextAlphaState = 1 @@ -426,16 +411,32 @@ 'ExtGState': self.alphaStateObject, 'Pattern': self.hatchObject, 'ProcSet': procsets } - self.writeObject(resourceObject, resources) + self.writeObject(self.resourceObject, resources) - # Start the content stream of the page + def newPage(self, width, height): + self.endStream() + + self.width, self.height = width, height + if rcParams['path.simplify']: + self.simplify = (width * 72, height * 72) + else: + self.simplify = None + contentObject = self.reserveObject('page contents') + thePage = { 'Type': Name('Page'), + 'Parent': self.pagesObject, + 'Resources': self.resourceObject, + 'MediaBox': [ 0, 0, 72*width, 72*height ], + 'Contents': contentObject } + pageObject = self.reserveObject('page') + self.writeObject(pageObject, thePage) + self.pageList.append(pageObject) + self.beginStream(contentObject.id, self.reserveObject('length of content stream')) def close(self): - # End the content stream and write out the various deferred - # objects self.endStream() + # Write out the various deferred objects self.writeFonts() self.writeObject(self.alphaStateObject, dict([(val[0], val[1]) @@ -449,6 +450,12 @@ self.writeObject(self.XObjectObject, xobjects) self.writeImages() self.writeMarkers() + self.writeObject(self.pagesObject, + { 'Type': Name('Pages'), + 'Kids': self.pageList, + 'Count': len(self.pageList) }) + + # Finalize the file self.writeXref() self.writeTrailer() if self.passed_in_file_object: @@ -471,8 +478,9 @@ self.currentstream = Stream(id, len, self, extra) def endStream(self): - self.currentstream.end() - self.currentstream = None + if self.currentstream is not None: + self.currentstream.end() + self.currentstream = None def fontName(self, fontprop): """ @@ -493,20 +501,27 @@ Fx = Name('F%d' % self.nextFont) self.fontNames[filename] = Fx self.nextFont += 1 + matplotlib.verbose.report( + 'Assigning font %s = %s' % (Fx, filename), + 'debug') return Fx def writeFonts(self): fonts = {} for filename, Fx in self.fontNames.items(): + matplotlib.verbose.report('Embedding font %s' % filename, 'debug') if filename.endswith('.afm'): # from pdf.use14corefonts + matplotlib.verbose.report('Writing AFM font', 'debug') fontdictObject = self._write_afm_font(filename) elif self.dviFontInfo.has_key(filename): # a Type 1 font from a dvi file; the filename is really the TeX name + matplotlib.verbose.report('Writing Type-1 font', 'debug') fontdictObject = self.embedType1(filename, self.dviFontInfo[filename]) else: # a normal TrueType font + matplotlib.verbose.report('Writing TrueType font', 'debug') realpath, stat_key = get_realpath_and_stat(filename) chars = self.used_characters.get(stat_key) if chars is not None and len(chars[1]): @@ -1143,8 +1158,7 @@ return cmds def writePath(self, path, transform): - cmds = self.pathOperations( - path, transform, self.simplify) + cmds = self.pathOperations(path, transform, self.simplify) self.output(*cmds) def reserveObject(self, name=''): @@ -1198,13 +1212,11 @@ truetype_font_cache = maxdict(50) afm_font_cache = maxdict(50) - def __init__(self, file, dpi, image_dpi): + def __init__(self, file, image_dpi): RendererBase.__init__(self) self.file = file self.gc = self.new_gc() - self.file.used_characters = self.used_characters = {} self.mathtext_parser = MathTextParser("Pdf") - self.dpi = dpi self.image_dpi = image_dpi self.tex_font_map = None @@ -1235,13 +1247,13 @@ else: fname = font.fname realpath, stat_key = get_realpath_and_stat(fname) - used_characters = self.used_characters.setdefault( + used_characters = self.file.used_characters.setdefault( stat_key, (realpath, set())) used_characters[1].update([ord(x) for x in s]) def merge_used_characters(self, other): for stat_key, (realpath, charset) in other.items(): - used_characters = self.used_characters.setdefault( + used_characters = self.file.used_characters.setdefault( stat_key, (realpath, set())) used_characters[1].update(charset) @@ -1299,7 +1311,7 @@ def draw_mathtext(self, gc, x, y, s, prop, angle): # TODO: fix positioning and encoding width, height, descent, glyphs, rects, used_characters = \ - self.mathtext_parser.parse(s, self.dpi, prop) + self.mathtext_parser.parse(s, 72, prop) self.merge_used_characters(used_characters) # When using Type 3 fonts, we can't use character codes higher @@ -1327,7 +1339,6 @@ self._setup_textpos(ox, oy, 0, 0, oldx, oldy) oldx, oldy = ox, oy if (fontname, fontsize) != prev_font: - fontsize *= self.dpi/72.0 self.file.output(self.file.fontName(fontname), fontsize, Op.selectfont) prev_font = fontname, fontsize @@ -1338,7 +1349,6 @@ # as XObjects using the 'Do' command. if global_fonttype == 3: for ox, oy, fontname, fontsize, num, symbol_name in glyphs: - fontsize *= self.dpi/72.0 if is_opentype_cff_font(fontname): fonttype = 42 else: @@ -1367,7 +1377,7 @@ texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() dvifile = texmanager.make_dvi(s, fontsize) - dvi = dviread.Dvi(dvifile, self.dpi) + dvi = dviread.Dvi(dvifile, 72) page = iter(dvi).next() dvi.close() @@ -1463,7 +1473,7 @@ self.check_gc(gc, gc._rgb) if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) - fontsize = prop.get_size_in_points() * self.dpi/72.0 + fontsize = prop.get_size_in_points() if rcParams['pdf.use14corefonts']: font = self._get_font_afm(prop) @@ -1593,14 +1603,14 @@ texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() dvifile = texmanager.make_dvi(s, fontsize) - dvi = dviread.Dvi(dvifile, self.dpi) + dvi = dviread.Dvi(dvifile, 72) page = iter(dvi).next() dvi.close() # A total height (including the descent) needs to be returned. return page.width, page.height+page.descent, page.descent if ismath: w, h, d, glyphs, rects, used_characters = \ - self.mathtext_parser.parse(s, self.dpi, prop) + self.mathtext_parser.parse(s, 72, prop) elif rcParams['pdf.use14corefonts']: font = self._get_font_afm(prop) @@ -1645,14 +1655,14 @@ self.truetype_font_cache[filename] = font self.truetype_font_cache[key] = font font.clear() - font.set_size(prop.get_size_in_points(), self.dpi) + font.set_size(prop.get_size_in_points(), 72) return font def flipy(self): return False def get_canvas_width_height(self): - return self.file.width / self.dpi, self.file.height / self.dpi + return self.file.width / 72.0, self.file.height / 72.0 def new_gc(self): return GraphicsContextPdf(self.file) @@ -1887,16 +1897,22 @@ return 'pdf' def print_pdf(self, filename, **kwargs): - ppi = 72 # Postscript points in an inch image_dpi = kwargs.get('dpi', 72) # dpi to use for images - self.figure.set_dpi(ppi) + self.figure.set_dpi(72) # there are 72 pdf points to an inch width, height = self.figure.get_size_inches() - file = PdfFile(width, height, ppi, filename) + if isinstance(filename, PdfFile): + file = filename + else: + file = PdfFile(filename) + file.newPage(width, height) renderer = MixedModeRenderer( - width, height, ppi, RendererPdf(file, ppi, image_dpi)) + width, height, 72, RendererPdf(file, image_dpi)) self.figure.draw(renderer) renderer.finalize() - file.close() + if file != filename: # we opened the file + file.close() + else: # multipage file; just finish off the page + file.endStream() class FigureManagerPdf(FigureManagerBase): pass This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2009-01-02 17:56:26
|
Revision: 6733 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6733&view=rev Author: mdboom Date: 2009-01-02 17:56:15 +0000 (Fri, 02 Jan 2009) Log Message: ----------- Merged revisions 6717,6732 via svnmerge from https://matplotlib.svn.sf.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6717 | jdh2358 | 2008-12-30 11:06:59 -0500 (Tue, 30 Dec 2008) | 1 line added rcparams import to path ........ r6732 | efiring | 2009-01-02 12:13:37 -0500 (Fri, 02 Jan 2009) | 2 lines Backport r6731 polar fixes from trunk. ........ Property Changed: ---------------- trunk/matplotlib/ trunk/matplotlib/doc/pyplots/README trunk/matplotlib/doc/sphinxext/gen_gallery.py trunk/matplotlib/doc/sphinxext/gen_rst.py Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6715 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6732 Modified: svn:mergeinfo - /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715 + /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732 Property changes on: trunk/matplotlib/doc/pyplots/README ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715 + /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732 Property changes on: trunk/matplotlib/doc/sphinxext/gen_gallery.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673,6714-6715 + /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673,6714-6715,6717-6732 Property changes on: trunk/matplotlib/doc/sphinxext/gen_rst.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_rst.py:6714-6715 + /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_rst.py:6714-6715,6717-6732 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2009-01-05 05:29:01
|
Revision: 6734 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6734&view=rev Author: leejjoon Date: 2009-01-05 05:28:57 +0000 (Mon, 05 Jan 2009) Log Message: ----------- optional use of preview.sty in usetex mode Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_agg.py trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/backends/backend_ps.py trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/lib/matplotlib/texmanager.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/usetex_baseline_test.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/CHANGELOG 2009-01-05 05:28:57 UTC (rev 6734) @@ -1,3 +1,5 @@ +2009-01-05 optional use of preview.sty in usetex mode. - JJL + 2009-01-02 Allow multipage pdf files. - JKS 2008-12-31 Improve pdf usetex by adding support for font effects Added: trunk/matplotlib/examples/pylab_examples/usetex_baseline_test.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/usetex_baseline_test.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/usetex_baseline_test.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -0,0 +1,76 @@ + +import matplotlib +import matplotlib.pyplot as plt +import matplotlib.axes as maxes + +class Axes(maxes.Axes): + """ + A hackish way to simultaneously draw texts w/ usetex=True and + usetex=False in the same figure. It does not work in the ps backend. + """ + def __init__(self, *kl, **kw): + self.usetex = kw.pop("usetex", "False") + self.preview = kw.pop("preview", "False") + + maxes.Axes.__init__(self, *kl, **kw) + + def draw(self, renderer): + usetex = plt.rcParams["text.usetex"] + preview = plt.rcParams["text.latex.preview"] + plt.rcParams["text.usetex"] = self.usetex + plt.rcParams["text.latex.preview"] = self.preview + + maxes.Axes.draw(self, renderer) + + plt.rcParams["text.usetex"] = usetex + plt.rcParams["text.latex.preview"] = preview + +Subplot = maxes.subplot_class_factory(Axes) + + +def test_window_extent(ax, usetex, preview): + + va = "baseline" + ax.xaxis.set_visible(False) + ax.yaxis.set_visible(False) + + + #t = ax.text(0., 0., r"mlp", va="baseline", size=150) + text_kw = dict(va=va, + size=50, + bbox=dict(pad=0., ec="k", fc="none")) + + + test_strings = ["lg", r"$\frac{1}{2}\pi$", + r"$p^{3^A}$", r"$p_{3_2}$"] + + ax.axvline(0, color="r") + + for i, s in enumerate(test_strings): + + ax.axhline(i, color="r") + ax.text(0., 3-i, s, **text_kw) + + ax.set_xlim(-0.1,1.1) + ax.set_ylim(-.8,3.9) + + + ax.set_title("usetex=%s\npreview=%s" % (str(usetex), str(preview))) + + + +F = plt.figure(figsize=(2.*3,6.5)) + +for i, usetex, preview in [[0, False, False], + [1, True, False], + [2, True, True]]: + ax = Subplot(F, 1, 3, i+1, usetex=usetex, preview=preview) + F.add_subplot(ax) + F.subplots_adjust(top=0.85) + + test_window_extent(ax, usetex=usetex, preview=preview) + + +plt.draw() +plt.show() + Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -146,10 +146,10 @@ # todo: handle props size = prop.get_size_in_points() texmanager = self.get_texmanager() - Z = texmanager.get_grey(s, size, self.dpi) - m,n = Z.shape - # TODO: descent of TeX text (I am imitating backend_ps here -JKS) - return n, m, 0 + fontsize = prop.get_size_in_points() + w, h, d = texmanager.get_text_width_height_descent(s, fontsize, + renderer=self) + return w, h, d if ismath: ox, oy, width, height, descent, fonts, used_characters = \ Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -612,7 +612,7 @@ if 0: flags |= 1 << 18 # TODO: force bold ft2font = FT2Font(fontfile) - + descriptor = { 'Type': Name('FontDescriptor'), 'FontName': Name(t1font.prop['FontName']), @@ -1602,12 +1602,10 @@ if rcParams['text.usetex']: texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() - dvifile = texmanager.make_dvi(s, fontsize) - dvi = dviread.Dvi(dvifile, 72) - page = iter(dvi).next() - dvi.close() - # A total height (including the descent) needs to be returned. - return page.width, page.height+page.descent, page.descent + w, h, d = texmanager.get_text_width_height_descent(s, fontsize, + renderer=self) + return w, h, d + if ismath: w, h, d, glyphs, rects, used_characters = \ self.mathtext_parser.parse(s, 72, prop) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -274,12 +274,9 @@ if rcParams['text.usetex']: texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() - l,b,r,t = texmanager.get_ps_bbox(s, fontsize) - w = (r-l) - h = (t-b) - # TODO: We need a way to get a good baseline from - # text.usetex - return w, h, 0 + w, h, d = texmanager.get_text_width_height_descent(s, fontsize, + renderer=self) + return w, h, d if ismath: width, height, descent, pswriter, used_characters = \ Modified: trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template =================================================================== --- trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/mpl-data/matplotlib.conf.template 2009-01-05 05:28:57 UTC (rev 6734) @@ -357,6 +357,8 @@ preamble = [] # a boolean unicode = False + # a boolean + preview = False [verbose] # a file name or 'sys.stdout' Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -386,6 +386,7 @@ 'text.usetex' : [False, validate_bool], 'text.latex.unicode' : [False, validate_bool], 'text.latex.preamble' : [[''], validate_stringlist], + 'text.latex.preview' : [False, validate_bool], 'text.dvipnghack' : [None, validate_bool_maybe_none], 'text.fontstyle' : ['normal', str], 'text.fontangle' : ['normal', str], Modified: trunk/matplotlib/lib/matplotlib/texmanager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/texmanager.py 2009-01-02 17:56:15 UTC (rev 6733) +++ trunk/matplotlib/lib/matplotlib/texmanager.py 2009-01-05 05:28:57 UTC (rev 6734) @@ -45,6 +45,8 @@ import matplotlib as mpl from matplotlib import rcParams from matplotlib._png import read_png +import matplotlib.dviread as dviread +import re DEBUG = False @@ -262,12 +264,83 @@ return texfile + + _re_vbox = re.compile(r"MatplotlibBox:\(([\d.]+)pt\+([\d.]+)pt\)x([\d.]+)pt") + + def make_tex_preview(self, tex, fontsize): + """ + Generate a tex file to render the tex string at a specific + font size. It uses the preview.sty to determin the dimension + (width, height, descent) of the output. + + returns the file name + """ + basefile = self.get_basefile(tex, fontsize) + texfile = '%s.tex'%basefile + fh = file(texfile, 'w') + custom_preamble = self.get_custom_preamble() + fontcmd = {'sans-serif' : r'{\sffamily %s}', + 'monospace' : r'{\ttfamily %s}'}.get(self.font_family, + r'{\rmfamily %s}') + tex = fontcmd % tex + + if rcParams['text.latex.unicode']: + unicode_preamble = """\usepackage{ucs} +\usepackage[utf8x]{inputenc}""" + else: + unicode_preamble = '' + + + + # newbox, setbox, immediate, etc. are used to find the box + # extent of the rendered text. + + + s = r"""\documentclass{article} +%s +%s +%s +\usepackage[active,showbox,tightpage]{preview} +%%\usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry} + +%% we override the default showbox as it is treated as an error and makes +%% the exit status not zero +\def\showbox#1{\immediate\write16{MatplotlibBox:(\the\ht#1+\the\dp#1)x\the\wd#1}} + +\begin{document} +\begin{preview} +{\fontsize{%f}{%f}%s} +\end{preview} +\end{document} +""" % (self._font_preamble, unicode_preamble, custom_preamble, + fontsize, fontsize*1.25, tex) + if rcParams['text.latex.unicode']: + fh.write(s.encode('utf8')) + else: + try: + fh.write(s) + except UnicodeEncodeError, err: + mpl.verbose.report("You are using unicode and latex, but have " + "not enabled the matplotlib 'text.latex.unicode' " + "rcParam.", 'helpful') + raise + + fh.close() + + return texfile + + def make_dvi(self, tex, fontsize): """ generates a dvi file containing latex's layout of tex string returns the file name """ + + + if rcParams['text.latex.preview']: + return self.make_dvi_preview(tex, fontsize) + basefile = self.get_basefile(tex, fontsize) dvifile = '%s.dvi'% basefile @@ -298,6 +371,55 @@ return dvifile + + def make_dvi_preview(self, tex, fontsize): + """ + generates a dvi file containing latex's layout of tex + string. It calls make_tex_preview() method and store the size + information (width, height, descent) in a separte file. + + returns the file name + """ + basefile = self.get_basefile(tex, fontsize) + dvifile = '%s.dvi'% basefile + baselinefile = '%s.baseline'% basefile + + if DEBUG or not os.path.exists(dvifile) or \ + not os.path.exists(baselinefile): + texfile = self.make_tex_preview(tex, fontsize) + outfile = basefile+'.output' + command = self._get_shell_cmd('cd "%s"'% self.texcache, + 'latex -interaction=nonstopmode %s > "%s"'\ + %(os.path.split(texfile)[-1], outfile)) + mpl.verbose.report(command, 'debug') + exit_status = os.system(command) + try: + fh = file(outfile) + report = fh.read() + fh.close() + + except IOError: + report = 'No latex error report available.' + if exit_status: + raise RuntimeError(('LaTeX was not able to process the following \ +string:\n%s\nHere is the full report generated by LaTeX: \n\n'% repr(tex)) + report) + else: mpl.verbose.report(report, 'debug') + + # find the box extent information in the latex output + # file and store them in ".baseline" file + m = TexManager._re_vbox.search(report) + open(basefile+'.baseline',"w").write(" ".join(m.groups())) + + for fname in glob.glob(basefile+'*'): + if fname.endswith('dvi'): pass + elif fname.endswith('tex'): pass + elif fname.endswith('baseline'): pass + else: + try: os.remove(fname) + except OSError: pass + + return dvifile + def make_png(self, tex, fontsize, dpi): """ generates a png file containing latex's rendering of tex string @@ -441,3 +563,37 @@ self.rgba_arrayd[key] = Z return Z + + + def get_text_width_height_descent(self, tex, fontsize, renderer=None): + """ + return width, heigth and descent of the text. + """ + + if renderer: + dpi_fraction = renderer.points_to_pixels(1.) + else: + dpi_fraction = 1. + + if rcParams['text.latex.preview']: + # use preview.sty + basefile = self.get_basefile(tex, fontsize) + baselinefile = '%s.baseline'% basefile + + + if DEBUG or not os.path.exists(baselinefile): + dvifile = self.make_dvi_preview(tex, fontsize) + + l = open(baselinefile).read().split() + height, depth, width = [float(l1)*dpi_fraction for l1 in l] + return width, height+depth, depth + + else: + # use dviread. It sometimes returns a wrong descent. + dvifile = self.make_dvi(tex, fontsize) + dvi = dviread.Dvi(dvifile, 72*dpi_fraction) + page = iter(dvi).next() + dvi.close() + # A total height (including the descent) needs to be returned. + return page.width, page.height+page.descent, page.descent + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2009-01-05 17:48:05
|
Revision: 6737 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6737&view=rev Author: jouni Date: 2009-01-05 17:48:01 +0000 (Mon, 05 Jan 2009) Log Message: ----------- Fix a bug in pdf usetex: allow using non-embedded fonts Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-05 14:47:08 UTC (rev 6736) +++ trunk/matplotlib/CHANGELOG 2009-01-05 17:48:01 UTC (rev 6737) @@ -1,3 +1,5 @@ +2009-01-05 Fix a bug in pdf usetex: allow using non-embedded fonts. - JKS + 2009-01-05 optional use of preview.sty in usetex mode. - JJL 2009-01-02 Allow multipage pdf files. - JKS Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-05 14:47:08 UTC (rev 6736) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-05 17:48:01 UTC (rev 6737) @@ -88,11 +88,9 @@ # * the alpha channel of images # * image compression could be improved (PDF supports png-like compression) # * encoding of fonts, including mathtext fonts and unicode support -# * Type 1 font support (i.e., "pdf.use_afm") # * TTF support has lots of small TODOs, e.g. how do you know if a font # is serif/sans-serif, or symbolic/non-symbolic? # * draw_markers, draw_line_collection, etc. -# * use_tex def fill(strings, linelen=75): """Make one string from sequence of strings, with whitespace @@ -518,7 +516,7 @@ elif self.dviFontInfo.has_key(filename): # a Type 1 font from a dvi file; the filename is really the TeX name matplotlib.verbose.report('Writing Type-1 font', 'debug') - fontdictObject = self.embedType1(filename, self.dviFontInfo[filename]) + fontdictObject = self.embedTeXFont(filename, self.dviFontInfo[filename]) else: # a normal TrueType font matplotlib.verbose.report('Writing TrueType font', 'debug') @@ -542,53 +540,62 @@ self.writeObject(fontdictObject, fontdict) return fontdictObject - def embedType1(self, texname, fontinfo): + def embedTeXFont(self, texname, fontinfo): matplotlib.verbose.report( - 'Embedding ' + texname + - ' which is the Type 1 font ' + (fontinfo.fontfile or '(none)') + - ' with encoding ' + (fontinfo.encodingfile or '(none)') + - ' and effects ' + `fontinfo.effects`, + 'Embedding TeX font ' + texname + ' - fontinfo=' + `fontinfo.__dict__`, 'debug') - t1font = type1font.Type1Font(fontinfo.fontfile) - if fontinfo.effects: - t1font = t1font.transform(fontinfo.effects) - - # Font descriptors may be shared between differently encoded - # Type-1 fonts, so only create a new descriptor if there is no - # existing descriptor for this font. - effects = (fontinfo.effects.get('slant', 0.0), fontinfo.effects.get('extend', 1.0)) - fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects)) - if fontdesc is None: - fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile) - self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc - # Widths widthsObject = self.reserveObject('font widths') - self.writeObject(widthsObject, fontinfo.widths) + self.writeObject(widthsObject, fontinfo.dvifont.widths) # Font dictionary fontdictObject = self.reserveObject('font dictionary') fontdict = { - 'Type': Name('Font'), - 'Subtype': Name('Type1'), - 'BaseFont': Name(t1font.prop['FontName']), - 'FirstChar': 0, - 'LastChar': len(fontinfo.widths) - 1, - 'Widths': widthsObject, - 'FontDescriptor': fontdesc, - } + 'Type': Name('Font'), + 'Subtype': Name('Type1'), + 'FirstChar': 0, + 'LastChar': len(fontinfo.dvifont.widths) - 1, + 'Widths': widthsObject, + } # Encoding (if needed) if fontinfo.encodingfile is not None: enc = dviread.Encoding(fontinfo.encodingfile) differencesArray = [ Name(ch) for ch in enc ] differencesArray = [ 0 ] + differencesArray - fontdict.update({ - 'Encoding': { 'Type': Name('Encoding'), - 'Differences': differencesArray }, - }) + fontdict['Encoding'] = \ + { 'Type': Name('Encoding'), + 'Differences': differencesArray } + # If no file is specified, stop short + if fontinfo.fontfile is None: + warnings.warn( + 'Because of TeX configuration (pdftex.map, see updmap ' + + 'option pdftexDownloadBase14) the font %s ' % fontinfo.basefont + + 'is not embedded. This is deprecated as of PDF 1.5 ' + + 'and it may cause the consumer application to show something ' + + 'that was not intended.') + fontdict['BaseFont'] = Name(fontinfo.basefont) + self.writeObject(fontdictObject, fontdict) + return fontdictObject + + # We have a font file to embed - read it in and apply any effects + t1font = type1font.Type1Font(fontinfo.fontfile) + if fontinfo.effects: + t1font = t1font.transform(fontinfo.effects) + fontdict['BaseFont'] = Name(t1font.prop['FontName']) + + # Font descriptors may be shared between differently encoded + # Type-1 fonts, so only create a new descriptor if there is no + # existing descriptor for this font. + effects = (fontinfo.effects.get('slant', 0.0), fontinfo.effects.get('extend', 1.0)) + fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects)) + if fontdesc is None: + fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile) + self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc + fontdict['FontDescriptor'] = fontdesc + self.writeObject(fontdictObject, fontdict) return fontdictObject @@ -1389,11 +1396,12 @@ pdfname = self.file.fontName(dvifont.texname) if not self.file.dviFontInfo.has_key(dvifont.texname): psfont = self.tex_font_mapping(dvifont.texname) + fontfile = psfont.filename self.file.dviFontInfo[dvifont.texname] = Bunch( fontfile=psfont.filename, + basefont=psfont.psname, encodingfile=psfont.encoding, effects=psfont.effects, - widths=dvifont.widths, dvifont=dvifont) seq += [['font', pdfname, dvifont.size]] oldfont = dvifont This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2009-01-06 08:07:20
|
Revision: 6739 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6739&view=rev Author: jouni Date: 2009-01-06 08:07:18 +0000 (Tue, 06 Jan 2009) Log Message: ----------- Create a PdfFile wrapper named PdfPages to act as the target of savefig to avoid saving figures in png format onto the file-like PdfPages object. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/api/api_changes.rst trunk/matplotlib/examples/pylab_examples/multipage_pdf.py trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-05 18:11:47 UTC (rev 6738) +++ trunk/matplotlib/CHANGELOG 2009-01-06 08:07:18 UTC (rev 6739) @@ -1,3 +1,6 @@ +2009-01-06 Change user-visible multipage pdf object to PdfPages to + avoid accidents with the file-like PdfFile. - JKS + 2009-01-05 Fix a bug in pdf usetex: allow using non-embedded fonts. - JKS 2009-01-05 optional use of preview.sty in usetex mode. - JJL Modified: trunk/matplotlib/doc/api/api_changes.rst =================================================================== --- trunk/matplotlib/doc/api/api_changes.rst 2009-01-05 18:11:47 UTC (rev 6738) +++ trunk/matplotlib/doc/api/api_changes.rst 2009-01-06 08:07:18 UTC (rev 6739) @@ -6,6 +6,10 @@ outward-facing API. If updating matplotlib breaks your scripts, this list may help describe what changes may be necessary in your code. +* You can now print several figures to one pdf file. See the docstrings + of the class :class:`matplotlib.backends.backend_pdf.PdfPages` for + more information. + * Removed configobj_ and `enthought.traits`_ packages, which are only required by the experimental traited config and are somewhat out of date. If needed, install them independently. Modified: trunk/matplotlib/examples/pylab_examples/multipage_pdf.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/multipage_pdf.py 2009-01-05 18:11:47 UTC (rev 6738) +++ trunk/matplotlib/examples/pylab_examples/multipage_pdf.py 2009-01-06 08:07:18 UTC (rev 6739) @@ -1,14 +1,17 @@ +# This is a demo of creating a pdf file with several pages. + import numpy as np import matplotlib -from matplotlib.backends.backend_pdf import PdfFile +from matplotlib.backends.backend_pdf import PdfPages from pylab import * -pdf = PdfFile('multipage_pdf.pdf') +# Create the PdfPages object to which we will save the pages: +pdf = PdfPages('multipage_pdf.pdf') figure(figsize=(3,3)) plot(range(7), [3,1,4,1,5,9,2], 'r-o') title('Page One') -savefig(pdf, format='pdf') +savefig(pdf, format='pdf') # note the format='pdf' argument! close() rc('text', usetex=True) @@ -16,14 +19,15 @@ x = np.arange(0,5,0.1) plot(x, np.sin(x), 'b-') title('Page Two') -savefig(pdf, format='pdf') +pdf.savefig() # here's another way - or you could do pdf.savefig(1) close() rc('text', usetex=False) -figure(figsize=(4,5)) +fig=figure(figsize=(4,5)) plot(x, x*x, 'ko') title('Page Three') -savefig(pdf, format='pdf') +pdf.savefig(fig) # or you can pass a Figure object to pdf.savefig close() +# Remember to close the object - otherwise the file will not be usable pdf.close() Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-05 18:11:47 UTC (rev 6738) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-06 08:07:18 UTC (rev 6739) @@ -1882,7 +1882,64 @@ manager = FigureManagerPdf(canvas, num) return manager +class PdfPages(object): + """ + A multi-page PDF file. + Use like this: + + # Initialize: + pdf_pages = PdfPages('foo.pdf') + + # As many times as you like, create a figure fig, then either: + fig.savefig(pdf_pages, format='pdf') # note the format argument! + # or: + pdf_pages.savefig(fig) + + # Once you are done, remember to close the object: + pdf_pages.close() + + (In reality PdfPages is a thin wrapper around PdfFile, in order to + avoid confusion when using savefig and forgetting the format + argument.) + """ + __slots__ = ('_file',) + + def __init__(self, filename): + """ + Create a new PdfPages object that will be written to the file + named *filename*. The file is opened at once and any older + file with the same name is overwritten. + """ + self._file = PdfFile(filename) + + def close(self): + """ + Finalize this object, making the underlying file a complete + PDF file. + """ + self._file.close() + self._file = None + + def savefig(self, figure=None, **kwargs): + """ + Save the Figure instance *figure* to this file as a new page. + If *figure* is a number, the figure instance is looked up by + number, and if *figure* is None, the active figure is saved. + Any other keyword arguments are passed to Figure.savefig. + """ + if isinstance(figure, Figure): + figure.savefig(self, format='pdf', **kwargs) + else: + if figure is None: + figureManager = Gcf.get_active() + else: + figureManager = Gcf.get_fig_manager(figure) + if figureManager is None: + raise ValueError, "No such figure: " + `figure` + else: + figureManager.canvas.figure.savefig(self, format='pdf') + class FigureCanvasPdf(FigureCanvasBase): """ The canvas the figure renders into. Calls the draw and print fig @@ -1905,8 +1962,8 @@ image_dpi = kwargs.get('dpi', 72) # dpi to use for images self.figure.set_dpi(72) # there are 72 pdf points to an inch width, height = self.figure.get_size_inches() - if isinstance(filename, PdfFile): - file = filename + if isinstance(filename, PdfPages): + file = filename._file else: file = PdfFile(filename) file.newPage(width, height) @@ -1914,10 +1971,10 @@ width, height, 72, RendererPdf(file, image_dpi)) self.figure.draw(renderer) renderer.finalize() - if file != filename: # we opened the file + if isinstance(filename, PdfPages): # finish off this page + file.endStream() + else: # we opened the file above; now finish it off file.close() - else: # multipage file; just finish off the page - file.endStream() class FigureManagerPdf(FigureManagerBase): pass This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2009-01-06 17:06:08
|
Revision: 6743 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6743&view=rev Author: jdh2358 Date: 2009-01-06 17:06:06 +0000 (Tue, 06 Jan 2009) Log Message: ----------- added marginal density option to hexbin Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/boilerplate.py trunk/matplotlib/examples/pylab_examples/hexbin_demo2.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/setupext.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-06 16:53:09 UTC (rev 6742) +++ trunk/matplotlib/CHANGELOG 2009-01-06 17:06:06 UTC (rev 6743) @@ -1,3 +1,6 @@ +2009-01-06 Added marginals kwarg to hexbin to plot marginal densities + JDH + 2009-01-06 Change user-visible multipage pdf object to PdfPages to avoid accidents with the file-like PdfFile. - JKS Modified: trunk/matplotlib/boilerplate.py =================================================================== --- trunk/matplotlib/boilerplate.py 2009-01-06 16:53:09 UTC (rev 6742) +++ trunk/matplotlib/boilerplate.py 2009-01-06 17:06:06 UTC (rev 6743) @@ -102,7 +102,7 @@ cmappable = { 'contour' : 'if ret._A is not None: gci._current = ret', 'contourf': 'if ret._A is not None: gci._current = ret', - 'hexbin' : 'gci._current = ret', + 'hexbin' : 'gci._current = ret[0]', 'scatter' : 'gci._current = ret', 'pcolor' : 'gci._current = ret', 'pcolormesh' : 'gci._current = ret', Modified: trunk/matplotlib/examples/pylab_examples/hexbin_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hexbin_demo2.py 2009-01-06 16:53:09 UTC (rev 6742) +++ trunk/matplotlib/examples/pylab_examples/hexbin_demo2.py 2009-01-06 17:06:06 UTC (rev 6743) @@ -39,13 +39,14 @@ gridsize=30 plt.subplot(211) -plt.hexbin(x,y, C=z, gridsize=gridsize ) +plt.hexbin(x,y, C=z, gridsize=gridsize, marginals=True) plt.axis([xmin, xmax, ymin, ymax]) cb = plt.colorbar() cb.set_label('mean value') + plt.subplot(212) -plt.hexbin(x,y, gridsize=gridsize ) +plt.hexbin(x,y, gridsize=gridsize) plt.axis([xmin, xmax, ymin, ymax]) cb = plt.colorbar() cb.set_label('N observations') Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2009-01-06 16:53:09 UTC (rev 6742) +++ trunk/matplotlib/lib/matplotlib/axes.py 2009-01-06 17:06:06 UTC (rev 6743) @@ -5212,7 +5212,7 @@ xscale = 'linear', yscale = 'linear', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, edgecolors='none', - reduce_C_function = np.mean, mincnt=None, + reduce_C_function = np.mean, mincnt=None, marginals=True, **kwargs): """ call signature:: @@ -5221,7 +5221,7 @@ xscale = 'linear', yscale = 'linear', cmap=None, norm=None, vmin=None, vmax=None, alpha=1.0, linewidths=None, edgecolors='none' - reduce_C_function = np.mean, mincnt=None, + reduce_C_function = np.mean, mincnt=None, marginals=True **kwargs) Make a hexagonal binning plot of *x* versus *y*, where *x*, @@ -5273,6 +5273,11 @@ If not None, only display cells with more than *mincnt* number of points in the cell + *marginals*: True|False + if marginals is True, plot the marginal density as + colormapped rectagles along the bottom of the x-axis and + left of the y-axis + Other keyword arguments controlling color mapping and normalization arguments: @@ -5320,8 +5325,11 @@ :class:`~matplotlib.collections.PolyCollection` instance; use :meth:`~matplotlib.collection.PolyCollection.get_array` on this :class:`~matplotlib.collections.PolyCollection` to get - the counts in each hexagon. + the counts in each hexagon.. If marginals is True, horizontal + bar and vertical bar (both PolyCollections) will be attached + to the return collection as attributes *hbar* and *vbar* + **Example:** .. plot:: mpl_examples/pylab_examples/hexbin_demo.py @@ -5331,8 +5339,10 @@ self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs) + x, y, C = cbook.delete_masked_points(x, y, C) + # Set the size of the hexagon grid if iterable(gridsize): nx, ny = gridsize @@ -5357,6 +5367,11 @@ xmax += padding sx = (xmax-xmin) / nx sy = (ymax-ymin) / ny + + if marginals: + xorig = x.copy() + yorig = y.copy() + x = (x-xmin)/sx y = (y-ymin)/sy ix1 = np.round(x).astype(int) @@ -5496,6 +5511,93 @@ # add the collection last self.add_collection(collection) + if not marginals: + return collection + + + if C is None: + C = np.ones(len(x)) + + def coarse_bin(x, y, coarse): + ind = coarse.searchsorted(x).clip(0, len(coarse)-1) + mus = np.zeros(len(coarse)) + for i in range(len(coarse)): + mu = reduce_C_function(y[ind==i]) + mus[i] = mu + return mus + + coarse = np.linspace(xmin, xmax, gridsize) + + xcoarse = coarse_bin(xorig, C, coarse) + valid = ~np.isnan(xcoarse) + verts, values = [], [] + for i,val in enumerate(xcoarse): + thismin = coarse[i] + if i<len(coarse)-1: + thismax = coarse[i+1] + else: + thismax = thismin + np.diff(coarse)[-1] + + if not valid[i]: continue + + verts.append([(thismin, 0), (thismin, 0.05), (thismax, 0.05), (thismax, 0)]) + values.append(val) + + values = np.array(values) + trans = mtransforms.blended_transform_factory( + self.transData, self.transAxes) + + + hbar = mcoll.PolyCollection(verts, transform=trans, edgecolors='face') + + hbar.set_array(values) + hbar.set_cmap(cmap) + hbar.set_norm(norm) + hbar.set_alpha(alpha) + hbar.update(kwargs) + self.add_collection(hbar) + + coarse = np.linspace(ymin, ymax, gridsize) + ycoarse = coarse_bin(yorig, C, coarse) + valid = ~np.isnan(ycoarse) + verts, values = [], [] + for i,val in enumerate(ycoarse): + thismin = coarse[i] + if i<len(coarse)-1: + thismax = coarse[i+1] + else: + thismax = thismin + np.diff(coarse)[-1] + if not valid[i]: continue + verts.append([(0, thismin), (0.0, thismax), (0.05, thismax), (0.05, thismin)]) + values.append(val) + + values = np.array(values) + + + trans = mtransforms.blended_transform_factory( + self.transAxes, self.transData) + + vbar = mcoll.PolyCollection(verts, transform=trans, edgecolors='face') + vbar.set_array(values) + vbar.set_cmap(cmap) + vbar.set_norm(norm) + vbar.set_alpha(alpha) + vbar.update(kwargs) + self.add_collection(vbar) + + + + collection.hbar = hbar + collection.vbar = vbar + + def on_changed(collection): + hbar.set_cmap(collection.get_cmap()) + hbar.set_clim(collection.get_clim()) + vbar.set_cmap(collection.get_cmap()) + vbar.set_clim(collection.get_clim()) + + collection.callbacksSM.connect('changed', on_changed) + return collection hexbin.__doc__ = cbook.dedent(hexbin.__doc__) % martist.kwdocd Modified: trunk/matplotlib/setupext.py =================================================================== --- trunk/matplotlib/setupext.py 2009-01-06 16:53:09 UTC (rev 6742) +++ trunk/matplotlib/setupext.py 2009-01-06 17:06:06 UTC (rev 6743) @@ -1066,8 +1066,7 @@ global BUILT_WINDOWING if BUILT_WINDOWING: return # only build it if you you haven't already module = Extension('matplotlib._windowing', - ['src/_windowing.cpp', - ], + ['src/_windowing.cpp'], ) add_windowing_flags(module) ext_modules.append(module) @@ -1341,7 +1340,7 @@ temp_copy('src/_backend_gdk.c', 'src/backend_gdk.c') module = Extension( 'matplotlib.backends._backend_gdk', - ['src/backend_gdk.c', ], + ['src/backend_gdk.c'], libraries = [], include_dirs=numpy_inc_dirs, ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2009-01-06 17:24:35
|
Revision: 6744 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6744&view=rev Author: mdboom Date: 2009-01-06 17:24:32 +0000 (Tue, 06 Jan 2009) Log Message: ----------- Add more hatch styles. Improve consistency across backends. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/hatch_demo.py trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/backends/backend_svg.py trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/path.py trunk/matplotlib/src/_backend_agg.cpp trunk/matplotlib/src/_backend_agg.h Added Paths: ----------- trunk/matplotlib/lib/matplotlib/hatch.py Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -6,17 +6,18 @@ fig = plt.figure() ax1 = fig.add_subplot(121) -ax1.annotate("Hatch is only supported in the PS and PDF backend", (1, 1), +ax1.annotate("Hatch is only supported in the PS, PDF, SVG and Agg backends", (1, 1), xytext=(0, 5), xycoords="axes fraction", textcoords="offset points", ha="center" ) -ax1.bar(range(1,5), range(1,5), color='gray', edgecolor='black', hatch="/") +ax1.bar(range(1,5), range(1,5), color='red', edgecolor='black', hatch="/") +ax1.bar(range(1,5), [6] * 4, bottom=range(1,5), color='blue', edgecolor='black', hatch='//') - ax2 = fig.add_subplot(122) -bars = ax2.bar(range(1,5), range(1,5), color='gray', ecolor='black') +bars = ax2.bar(range(1,5), range(1,5), color='yellow', ecolor='black') + \ + ax2.bar(range(1, 5), [6] * 4, bottom=range(1,5), color='green', ecolor='black') -patterns = ('/', '+', 'x', '\\') +patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.') for bar, pattern in zip(bars, patterns): bar.set_hatch(pattern) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -557,7 +557,7 @@ 'FirstChar': 0, 'LastChar': len(fontinfo.dvifont.widths) - 1, 'Widths': widthsObject, - } + } # Encoding (if needed) if fontinfo.encodingfile is not None: @@ -595,7 +595,7 @@ fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile) self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc fontdict['FontDescriptor'] = fontdesc - + self.writeObject(fontdictObject, fontdict) return fontdictObject @@ -1749,7 +1749,6 @@ else: return [Name('DeviceRGB'), Op.setcolorspace_nonstroke] else: - hatch = hatch.lower() hatch_style = (self._rgb, self._fillcolor, hatch) name = self.file.hatchPattern(hatch_style) return [Name('Pattern'), Op.setcolorspace_nonstroke, Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -95,17 +95,22 @@ """ Create a new hatch pattern """ - HATCH_SIZE = 144 - dictkey = (gc.get_hatch().lower(), rgbFace, gc.get_rgb()) + HATCH_SIZE = 72 + dictkey = (gc.get_hatch(), rgbFace, gc.get_rgb()) id = self._hatchd.get(dictkey) if id is None: id = 'h%s' % md5(str(dictkey)).hexdigest() self._svgwriter.write('<defs>\n <pattern id="%s" ' % id) self._svgwriter.write('patternUnits="userSpaceOnUse" x="0" y="0" ') self._svgwriter.write(' width="%d" height="%d" >\n' % (HATCH_SIZE, HATCH_SIZE)) - path_data = self._convert_path(gc.get_hatch_path(), Affine2D().scale(144)) + path_data = self._convert_path( + gc.get_hatch_path(), + Affine2D().scale(HATCH_SIZE).scale(1.0, -1.0).translate(0, HATCH_SIZE)) + self._svgwriter.write( + '<rect x="0" y="0" width="%d" height="%d" fill="%s"/>' % + (HATCH_SIZE+1, HATCH_SIZE+1, rgb2hex(rgbFace))) path = '<path d="%s" fill="%s" stroke="%s" stroke-width="1.0"/>' % ( - path_data, rgb2hex(rgbFace[:3]), rgb2hex(gc.get_rgb()[:3])) + path_data, rgb2hex(gc.get_rgb()[:3]), rgb2hex(gc.get_rgb()[:3])) self._svgwriter.write(path) self._svgwriter.write('\n </pattern>\n</defs>') self._hatchd[dictkey] = id Added: trunk/matplotlib/lib/matplotlib/hatch.py =================================================================== --- trunk/matplotlib/lib/matplotlib/hatch.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/hatch.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -0,0 +1,194 @@ +""" +Contains a classes for generating hatch patterns. +""" + +import numpy as np +from matplotlib.path import Path + +class HatchPatternBase: + """ + The base class for a hatch pattern. + """ + pass + +class HorizontalHatch(HatchPatternBase): + def __init__(self, hatch, density): + self.num_lines = (hatch.count('-') + hatch.count('+')) * density + self.num_vertices = self.num_lines * 2 + + def set_vertices_and_codes(self, vertices, codes): + steps = np.linspace(0.0, 1.0, self.num_lines, False) + vertices[0::2, 0] = 0.0 + vertices[0::2, 1] = steps + vertices[1::2, 0] = 1.0 + vertices[1::2, 1] = steps + codes[0::2] = Path.MOVETO + codes[1::2] = Path.LINETO + +class VerticalHatch(HatchPatternBase): + def __init__(self, hatch, density): + self.num_lines = (hatch.count('|') + hatch.count('+')) * density + self.num_vertices = self.num_lines * 2 + + def set_vertices_and_codes(self, vertices, codes): + steps = np.linspace(0.0, 1.0, self.num_lines, False) + vertices[0::2, 0] = steps + vertices[0::2, 1] = 0.0 + vertices[1::2, 0] = steps + vertices[1::2, 1] = 1.0 + codes[0::2] = Path.MOVETO + codes[1::2] = Path.LINETO + +class NorthEastHatch(HatchPatternBase): + def __init__(self, hatch, density): + self.num_lines = (hatch.count('/') + hatch.count('x') + hatch.count('X')) * density + self.num_vertices = self.num_lines * 4 + + def set_vertices_and_codes(self, vertices, codes): + steps = np.linspace(0.0, 1.0, self.num_lines, False) + rev_steps = 1.0 - steps + vertices[0::4, 0] = 0.0 + vertices[0::4, 1] = steps + vertices[1::4, 0] = rev_steps + vertices[1::4, 1] = 1.0 + vertices[2::4, 0] = rev_steps + vertices[2::4, 1] = 0.0 + vertices[3::4, 0] = 1.0 + vertices[3::4, 1] = steps + codes[0::2] = Path.MOVETO + codes[1::2] = Path.LINETO + +class SouthEastHatch(HatchPatternBase): + def __init__(self, hatch, density): + self.num_lines = (hatch.count('\\') + hatch.count('x') + hatch.count('X')) * density + self.num_vertices = self.num_lines * 4 + + def set_vertices_and_codes(self, vertices, codes): + steps = np.linspace(0.0, 1.0, self.num_lines, False) + vertices[0::4, 0] = 1.0 + vertices[0::4, 1] = steps + vertices[1::4, 0] = steps + vertices[1::4, 1] = 1.0 + vertices[2::4, 0] = steps + vertices[2::4, 1] = 0.0 + vertices[3::4, 0] = 0.0 + vertices[3::4, 1] = steps + codes[0::2] = Path.MOVETO + codes[1::2] = Path.LINETO + +class Shapes(HatchPatternBase): + filled = False + def __init__(self, hatch, density): + if self.num_rows == 0: + self.num_shapes = 0 + self.num_vertices = 0 + else: + self.num_shapes = ((self.num_rows / 2 + 1) * (self.num_rows + 1) + + (self.num_rows / 2) * (self.num_rows)) + self.num_vertices = (self.num_shapes * + len(self.shape_vertices) * + (self.filled and 1 or 2)) + + def set_vertices_and_codes(self, vertices, codes): + offset = 1.0 / self.num_rows + shape_vertices = self.shape_vertices * offset * self.size + if not self.filled: + inner_vertices = shape_vertices[::-1] * 0.9 + shape_codes = self.shape_codes + shape_size = len(shape_vertices) + + cursor = 0 + for row in xrange(self.num_rows + 1): + if row % 2 == 0: + cols = np.linspace(0.0, 1.0, self.num_rows + 1, True) + else: + cols = np.linspace(offset / 2.0, 1.0 - offset / 2.0, self.num_rows, True) + row_pos = row * offset + for col_pos in cols: + vertices[cursor:cursor+shape_size] = shape_vertices + (col_pos, row_pos) + codes[cursor:cursor+shape_size] = shape_codes + cursor += shape_size + if not self.filled: + vertices[cursor:cursor+shape_size] = inner_vertices + (col_pos, row_pos) + codes[cursor:cursor+shape_size] = shape_codes + cursor += shape_size + +class Circles(Shapes): + def __init__(self, hatch, density): + path = Path.unit_circle() + self.shape_vertices = path.vertices + self.shape_codes = path.codes + Shapes.__init__(self, hatch, density) + +class SmallCircles(Circles): + size = 0.2 + + def __init__(self, hatch, density): + self.num_rows = (hatch.count('o')) * density + Circles.__init__(self, hatch, density) + +class LargeCircles(Circles): + size = 0.35 + + def __init__(self, hatch, density): + self.num_rows = (hatch.count('O')) * density + Circles.__init__(self, hatch, density) + +class SmallFilledCircles(SmallCircles): + size = 0.1 + filled = True + + def __init__(self, hatch, density): + self.num_rows = (hatch.count('.')) * density + Circles.__init__(self, hatch, density) + +class Stars(Shapes): + size = 1.0 / 3.0 + filled = True + + def __init__(self, hatch, density): + self.num_rows = (hatch.count('*')) * density + path = Path.unit_regular_star(5) + self.shape_vertices = path.vertices + self.shape_codes = np.ones(len(self.shape_vertices)) * Path.LINETO + self.shape_codes[0] = Path.MOVETO + Shapes.__init__(self, hatch, density) + +_hatch_types = [ + HorizontalHatch, + VerticalHatch, + NorthEastHatch, + SouthEastHatch, + SmallCircles, + LargeCircles, + SmallFilledCircles, + Stars + ] + +def get_path(hatchpattern, density=6): + """ + Given a hatch specifier, *hatchpattern*, generates Path to render + the hatch in a unit square. *density* is the number of lines per + unit square. + """ + size = 1.0 + density = int(density) + + patterns = [hatch_type(hatchpattern, density) for hatch_type in _hatch_types] + num_vertices = sum([pattern.num_vertices for pattern in patterns]) + + if num_vertices == 0: + return Path(np.empty((0, 2))) + + vertices = np.empty((num_vertices, 2)) + codes = np.empty((num_vertices,), np.uint8) + + cursor = 0 + for pattern in patterns: + if pattern.num_vertices != 0: + vertices_chunk = vertices[cursor:cursor + pattern.num_vertices] + codes_chunk = codes[cursor:cursor + pattern.num_vertices] + pattern.set_vertices_and_codes(vertices_chunk, codes_chunk) + cursor += pattern.num_vertices + + return Path(vertices, codes) Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/lib/matplotlib/patches.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -238,19 +238,23 @@ \ - back diagonal | - vertical - - horizontal - # - crossed + + - crossed x - crossed diagonal + o - small circle + O - large circle + . - dots + * - stars Letters can be combined, in which case all the specified hatchings are done. If same letter repeats, it increases the - density of hatching in that direction. + density of hatching of that pattern. CURRENT LIMITATIONS: 1. Hatching is supported in the PostScript, PDF, SVG and Agg backends only. - ACCEPTS: [ '/' | '\\' | '|' | '-' | '#' | 'x' ] (ps & pdf backend only) + ACCEPTS: [ '/' | '\\' | '|' | '-' | '+' | 'x' | 'o' | 'O' | '.' | ' *' ] """ self._hatch = hatch Modified: trunk/matplotlib/lib/matplotlib/path.py =================================================================== --- trunk/matplotlib/lib/matplotlib/path.py 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/lib/matplotlib/path.py 2009-01-06 17:24:32 UTC (rev 6744) @@ -572,87 +572,17 @@ can be used in a repeated hatching pattern. *density* is the number of lines per unit square. """ + from matplotlib.hatch import get_path + if hatchpattern is None: return None - hatch = hatchpattern.lower() - hatch_path = cls._hatch_dict.get((hatch, density)) + hatch_path = cls._hatch_dict.get((hatchpattern, density)) if hatch_path is not None: return hatch_path - size = 1.0 - density = int(density) - counts = [ - hatch.count('-') + hatch.count('+'), - hatch.count('/') + hatch.count('x'), - hatch.count('|') + hatch.count('+'), - hatch.count('\\') + hatch.count('x') - ] - - if sum(counts) == 0: - return cls([]) - - counts = [x * density for x in counts] - - num_vertices = (counts[0] * 2 + counts[1] * 4 + - counts[2] * 2 + counts[3] * 4) - vertices = np.empty((num_vertices, 2)) - codes = np.empty((num_vertices,), cls.code_type) - codes[0::2] = cls.MOVETO - codes[1::2] = cls.LINETO - - cursor = 0 - - # - horizontal - if counts[0]: - vertices_chunk = vertices[cursor:cursor + counts[0] * 2] - cursor += counts[0] * 2 - steps = np.linspace(0.0, 1.0, counts[0], False) - vertices_chunk[0::2, 0] = 0.0 - vertices_chunk[0::2, 1] = steps - vertices_chunk[1::2, 0] = size - vertices_chunk[1::2, 1] = steps - - # / ne - if counts[1]: - vertices_chunk = vertices[cursor:cursor + counts[1] * 4] - cursor += counts[1] * 4 - steps = np.linspace(0.0, 1.0, counts[1], False) - vertices_chunk[0::4, 0] = 0.0 - vertices_chunk[0::4, 1] = steps - vertices_chunk[1::4, 0] = size - steps - vertices_chunk[1::4, 1] = size - vertices_chunk[2::4, 0] = size - steps - vertices_chunk[2::4, 1] = 0.0 - vertices_chunk[3::4, 0] = size - vertices_chunk[3::4, 1] = steps - - # | vertical - if counts[2]: - vertices_chunk = vertices[cursor:cursor + counts[2] * 2] - cursor += counts[2] * 2 - steps = np.linspace(0.0, 1.0, counts[2], False) - vertices_chunk[0::2, 0] = steps - vertices_chunk[0::2, 1] = 0.0 - vertices_chunk[1::2, 0] = steps - vertices_chunk[1::2, 1] = size - - # \ se - if counts[3]: - vertices_chunk = vertices[cursor:cursor + counts[3] * 4] - cursor += counts[3] * 4 - steps = np.linspace(0.0, 1.0, counts[3], False) - vertices_chunk[0::4, 0] = size - vertices_chunk[0::4, 1] = steps - vertices_chunk[1::4, 0] = steps - vertices_chunk[1::4, 1] = size - vertices_chunk[2::4, 0] = steps - vertices_chunk[2::4, 1] = 0.0 - vertices_chunk[3::4, 0] = 0.0 - vertices_chunk[3::4, 1] = steps - - hatch_path = cls(vertices, codes) - cls._hatch_dict[(hatch, density)] = hatch_path + hatch_path = get_path(hatchpattern, density) + cls._hatch_dict[(hatchpattern, density)] = hatch_path return hatch_path hatch = classmethod(hatch) Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/src/_backend_agg.cpp 2009-01-06 17:24:32 UTC (rev 6744) @@ -901,18 +901,20 @@ // Create and transform the path typedef agg::conv_transform<PathIterator> hatch_path_trans_t; typedef SimplifyPath<hatch_path_trans_t> hatch_path_simplify_t; - typedef agg::conv_stroke<hatch_path_simplify_t> hatch_path_stroke_t; + typedef agg::conv_curve<hatch_path_simplify_t> hatch_path_curve_t; + typedef agg::conv_stroke<hatch_path_curve_t> hatch_path_stroke_t; PathIterator hatch_path(gc.hatchpath); agg::trans_affine hatch_trans; + hatch_trans *= agg::trans_affine_scaling(1.0, -1.0); + hatch_trans *= agg::trans_affine_translation(0.0, 1.0); hatch_trans *= agg::trans_affine_scaling(HATCH_SIZE, HATCH_SIZE); hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans); - hatch_path_simplify_t hatch_path_simplify - (hatch_path_trans, true, false, HATCH_SIZE, HATCH_SIZE); - hatch_path_stroke_t hatch_path_stroke(hatch_path_simplify); + hatch_path_simplify_t hatch_path_simplify(hatch_path_trans, false, false, HATCH_SIZE, HATCH_SIZE); + hatch_path_curve_t hatch_path_curve(hatch_path_simplify); + hatch_path_stroke_t hatch_path_stroke(hatch_path_curve); hatch_path_stroke.width(1.0); hatch_path_stroke.line_cap(agg::square_cap); - theRasterizer.add_path(hatch_path_stroke); // Render the path into the hatch buffer pixfmt hatch_img_pixf(hatchRenderingBuffer); @@ -920,7 +922,11 @@ renderer_aa rs(rb); rb.clear(agg::rgba(0.0, 0.0, 0.0, 0.0)); rs.color(gc.color); + + theRasterizer.add_path(hatch_path_curve); agg::render_scanlines(theRasterizer, slineP8, rs); + theRasterizer.add_path(hatch_path_stroke); + agg::render_scanlines(theRasterizer, slineP8, rs); // Put clipping back on, if originally set on entry to this // function Modified: trunk/matplotlib/src/_backend_agg.h =================================================================== --- trunk/matplotlib/src/_backend_agg.h 2009-01-06 17:06:06 UTC (rev 6743) +++ trunk/matplotlib/src/_backend_agg.h 2009-01-06 17:24:32 UTC (rev 6744) @@ -208,9 +208,7 @@ Py::Object lastclippath; agg::trans_affine lastclippath_transform; - // HATCH_SIZE should be a power of 2, to take advantage of Agg's - // fast pattern rendering - static const size_t HATCH_SIZE = 128; + static const size_t HATCH_SIZE = 72; agg::int8u hatchBuffer[HATCH_SIZE * HATCH_SIZE * 4]; agg::rendering_buffer hatchRenderingBuffer; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mme...@us...> - 2009-01-06 18:59:34
|
Revision: 6745 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6745&view=rev Author: mmetz_bn Date: 2009-01-06 18:59:31 +0000 (Tue, 06 Jan 2009) Log Message: ----------- Fault tolerant handling of linestyles when too many Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/contour.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-06 17:24:32 UTC (rev 6744) +++ trunk/matplotlib/CHANGELOG 2009-01-06 18:59:31 UTC (rev 6745) @@ -1,3 +1,5 @@ +2009-01-06 Be fault tolerant when len(linestyles)>NLev in contour. - MM + 2009-01-06 Added marginals kwarg to hexbin to plot marginal densities JDH Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 17:24:32 UTC (rev 6744) +++ trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 18:59:31 UTC (rev 6745) @@ -860,8 +860,10 @@ else: if cbook.is_string_like(linestyles): tlinestyles = [linestyles] * Nlev - elif cbook.iterable(linestyles) and len(linestyles) <= Nlev: + elif cbook.iterable(linestyles) and len(linestyles) < Nlev: tlinestyles = list(linestyles) * int(np.ceil(Nlev/len(linestyles))) + elif cbook.iterable(linestyles): # len(linestyles) >= Nlev + tlinestyles = list(linestyles)[:Nlev] return tlinestyles def get_alpha(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2009-01-06 19:22:10
|
Revision: 6746 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6746&view=rev Author: efiring Date: 2009-01-06 19:22:09 +0000 (Tue, 06 Jan 2009) Log Message: ----------- Fix bug in setting of negative contour linestyles. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/contour.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-06 18:59:31 UTC (rev 6745) +++ trunk/matplotlib/CHANGELOG 2009-01-06 19:22:09 UTC (rev 6746) @@ -1,6 +1,8 @@ +2009-01-06 Fix bug in setting of dashed negative contours. - EF + 2009-01-06 Be fault tolerant when len(linestyles)>NLev in contour. - MM -2009-01-06 Added marginals kwarg to hexbin to plot marginal densities +2009-01-06 Added marginals kwarg to hexbin to plot marginal densities JDH 2009-01-06 Change user-visible multipage pdf object to PdfPages to Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 18:59:31 UTC (rev 6745) +++ trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 19:22:09 UTC (rev 6746) @@ -535,7 +535,7 @@ self.levels = kwargs.get('levels', None) self.filled = kwargs.get('filled', False) self.linewidths = kwargs.get('linewidths', None) - self.linestyles = kwargs.get('linestyles', 'solid') + self.linestyles = kwargs.get('linestyles', None) self.alpha = kwargs.get('alpha', 1.0) self.origin = kwargs.get('origin', None) @@ -613,9 +613,6 @@ linestyle = lstyle, alpha=self.alpha) - if level < 0.0 and self.monochrome: - ls = mpl.rcParams['contour.negative_linestyle'] - col.set_linestyle(ls) col.set_label('_nolegend_') self.ax.add_collection(col, False) self.collections.append(col) @@ -857,6 +854,11 @@ Nlev = len(self.levels) if linestyles is None: tlinestyles = ['solid'] * Nlev + if self.monochrome: + neg_ls = mpl.rcParams['contour.negative_linestyle'] + for i, lev in enumerate(self.levels): + if lev < 0.0: + tlinestyles[i] = neg_ls else: if cbook.is_string_like(linestyles): tlinestyles = [linestyles] * Nlev This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2009-01-06 20:57:30
|
Revision: 6755 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6755&view=rev Author: mdboom Date: 2009-01-06 20:57:28 +0000 (Tue, 06 Jan 2009) Log Message: ----------- Merged revisions 6752,6754 via svnmerge from https://matplotlib.svn.sf.net/svnroot/matplotlib/branches/v0_98_5_maint ........ r6752 | efiring | 2009-01-06 14:56:03 -0500 (Tue, 06 Jan 2009) | 2 lines Backport 6745, 6746 from trunk ........ r6754 | mdboom | 2009-01-06 15:51:53 -0500 (Tue, 06 Jan 2009) | 1 line Update git instructions with information about working on branches. ........ Modified Paths: -------------- trunk/matplotlib/doc/devel/coding_guide.rst trunk/matplotlib/lib/matplotlib/contour.py Property Changed: ---------------- trunk/matplotlib/ trunk/matplotlib/doc/pyplots/README trunk/matplotlib/doc/sphinxext/gen_gallery.py trunk/matplotlib/doc/sphinxext/gen_rst.py Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6732 + /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6754 Modified: svn:mergeinfo - /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732 + /branches/v0_91_maint:5753-5771 /branches/v0_98_5_maint:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732,6752-6754 Modified: trunk/matplotlib/doc/devel/coding_guide.rst =================================================================== --- trunk/matplotlib/doc/devel/coding_guide.rst 2009-01-06 20:51:53 UTC (rev 6754) +++ trunk/matplotlib/doc/devel/coding_guide.rst 2009-01-06 20:57:28 UTC (rev 6755) @@ -174,7 +174,7 @@ cd mpl.git git config --add remote.origin.fetch +refs/remotes/*:refs/remotes/* git fetch - git svn init --trunk=trunk/matplotlib --tags=tags https://matplotlib.svn.sourceforge.net/svnroot/matplotlib + git svn init --branches=branches --trunk=trunk/matplotlib --tags=tags https://matplotlib.svn.sourceforge.net/svnroot/matplotlib # Now just get the latest svn revisions from the SourceForge SVN repository git svn fetch -r 6300:HEAD @@ -224,6 +224,37 @@ git checkout whizbang-branch git rebase master +Working on a maintenance branch from git +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The matplotlib maintenance branches are also available through git. +(Note that the ``git svn init`` line in the instructions above was +updated to make this possible. If you created your git mirror without +a ``--branches`` option, you will need to perform all of the steps +again in a new directory). + +You can see which branches are available with:: + + git branch -a + +To switch your working copy to the 0.98.5 maintenance branch:: + + git checkout v0_98_5_maint + +Then you probably want to (as above) create a new local branch based +on that branch:: + + git checkout -b whizbang-branch + +When you ``git svn dcommit`` from a maintenance branch, it will commit +to that branch, not to the trunk. + +While it should theoretically be possible to perform merges from a git +maintenance branch to a git trunk and then commit those changes back +to the SVN trunk, I have yet to find the magic incantation to make +that work. However, svnmerge as described `above <svn-merge>`_ can be +used and in fact works quite well. + A note about git write access ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Property changes on: trunk/matplotlib/doc/pyplots/README ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732 + /branches/v0_98_5_maint/doc/pyplots/README:6581,6585,6587,6589-6609,6614,6616,6625,6652,6660-6662,6672-6673,6714-6715,6717-6732,6752-6754 Property changes on: trunk/matplotlib/doc/sphinxext/gen_gallery.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673,6714-6715,6717-6732 + /branches/v0_91_maint/doc/_templates/gen_gallery.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_gallery.py:6660-6662,6672-6673,6714-6715,6717-6732,6752-6754 Property changes on: trunk/matplotlib/doc/sphinxext/gen_rst.py ___________________________________________________________________ Modified: svn:mergeinfo - /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_rst.py:6714-6715,6717-6732 + /branches/v0_91_maint/doc/examples/gen_rst.py:5753-5771 /branches/v0_98_5_maint/doc/sphinxext/gen_rst.py:6714-6715,6717-6732,6752-6754 Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 20:51:53 UTC (rev 6754) +++ trunk/matplotlib/lib/matplotlib/contour.py 2009-01-06 20:57:28 UTC (rev 6755) @@ -875,6 +875,8 @@ tlinestyles = tlinestyles[:Nlev] else: raise ValueError("Unrecognized type for linestyles kwarg") + elif cbook.iterable(linestyles): # len(linestyles) >= Nlev + tlinestyles = list(linestyles)[:Nlev] return tlinestyles def get_alpha(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2009-01-10 20:04:03
|
Revision: 6771 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6771&view=rev Author: jdh2358 Date: 2009-01-10 20:03:59 +0000 (Sat, 10 Jan 2009) Log Message: ----------- applied michiel's hatch patch for macosx sf id 2497785 Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/examples/pylab_examples/hatch_demo.py trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py trunk/matplotlib/src/_macosx.m Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-08 22:11:09 UTC (rev 6770) +++ trunk/matplotlib/CHANGELOG 2009-01-10 20:03:59 UTC (rev 6771) @@ -1,3 +1,6 @@ +2009-01-10 Applied Michiel's hatch patch for macosx backend. Closes + sf patch 2497785 - JDH + 2009-01-06 Fix bug in setting of dashed negative contours. - EF 2009-01-06 Be fault tolerant when len(linestyles)>NLev in contour. - MM Modified: trunk/matplotlib/examples/pylab_examples/hatch_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2009-01-08 22:11:09 UTC (rev 6770) +++ trunk/matplotlib/examples/pylab_examples/hatch_demo.py 2009-01-10 20:03:59 UTC (rev 6771) @@ -6,10 +6,6 @@ fig = plt.figure() ax1 = fig.add_subplot(121) -ax1.annotate("Hatch is only supported in the PS, PDF, SVG and Agg backends", (1, 1), - xytext=(0, 5), - xycoords="axes fraction", textcoords="offset points", ha="center" - ) ax1.bar(range(1,5), range(1,5), color='red', edgecolor='black', hatch="/") ax1.bar(range(1,5), [6] * 4, bottom=range(1,5), color='blue', edgecolor='black', hatch='//') Modified: trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2009-01-08 22:11:09 UTC (rev 6770) +++ trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2009-01-10 20:03:59 UTC (rev 6771) @@ -77,6 +77,7 @@ def new_gc(self): self.gc.reset() + self.gc.set_hatch(None) return self.gc def draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None): @@ -166,10 +167,14 @@ _macosx.GraphicsContext.__init__(self) def set_foreground(self, fg, isRGB=False): - if not isRGB: - fg = colorConverter.to_rgb(fg) - _macosx.GraphicsContext.set_foreground(self, fg) + GraphicsContextBase.set_foreground(self, fg, isRGB) + rgb = self.get_rgb() + _macosx.GraphicsContext.set_foreground(self, rgb[:3]) + def set_graylevel(self, fg): + GraphicsContextBase.set_graylevel(self, fg) + _macosx.GraphicsContext.set_graylevel(self, fg) + def set_clip_rectangle(self, box): GraphicsContextBase.set_clip_rectangle(self, box) if not box: return Modified: trunk/matplotlib/src/_macosx.m =================================================================== --- trunk/matplotlib/src/_macosx.m 2009-01-08 22:11:09 UTC (rev 6770) +++ trunk/matplotlib/src/_macosx.m 2009-01-10 20:03:59 UTC (rev 6771) @@ -31,6 +31,10 @@ #define CURVE3 3 #define CURVE4 4 #define CLOSEPOLY 5 + +/* Hatching */ +#define HATCH_SIZE 72 + /* -------------------------- Helper function ---------------------------- */ static void stdin_ready(CFReadStreamRef readStream, CFStreamEventType eventType, void* context) @@ -203,6 +207,269 @@ PyErr_WarnEx(PyExc_RuntimeWarning, "ATSUDisposeTextLayout failed", 1); } +static int +_draw_path(CGContextRef cr, PyObject* path, CGAffineTransform affine) +{ + CGPoint point; + + PyObject* vertices = PyObject_GetAttrString(path, "vertices"); + if (vertices==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no vertices"); + return -1; + } + Py_DECREF(vertices); /* Don't keep a reference here */ + + PyObject* codes = PyObject_GetAttrString(path, "codes"); + if (codes==NULL) + { + PyErr_SetString(PyExc_AttributeError, "path has no codes"); + return -1; + } + Py_DECREF(codes); /* Don't keep a reference here */ + + PyArrayObject* coordinates; + coordinates = (PyArrayObject*)PyArray_FromObject(vertices, + NPY_DOUBLE, 2, 2); + if (!coordinates) + { + PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); + return -1; + } + + if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid vertices array"); + return -1; + } + + npy_intp n = PyArray_DIM(coordinates, 0); + + if (n==0) /* Nothing to do here */ + { + Py_DECREF(coordinates); + return 0; + } + + PyArrayObject* codelist = NULL; + if (codes != Py_None) + { + codelist = (PyArrayObject*)PyArray_FromObject(codes, + NPY_UINT8, 1, 1); + if (!codelist) + { + Py_DECREF(coordinates); + PyErr_SetString(PyExc_ValueError, "invalid codes array"); + return -1; + } + } + + if (codelist==NULL) + { + npy_intp i; + npy_uint8 code = MOVETO; + for (i = 0; i < n; i++) + { + point.x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); + point.y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); + if (isnan(point.x) || isnan(point.y)) + { + code = MOVETO; + } + else + { + point = CGPointApplyAffineTransform(point, affine); + switch (code) + { + case MOVETO: + CGContextMoveToPoint(cr, point.x, point.y); + break; + case LINETO: + CGContextAddLineToPoint(cr, point.x, point.y); + break; + } + code = LINETO; + } + } + } + else + { + npy_intp i = 0; + BOOL was_nan = false; + npy_uint8 code; + CGFloat x1, y1, x2, y2, x3, y3; + while (i < n) + { + code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); + if (code == CLOSEPOLY) + { + CGContextClosePath(cr); + i++; + } + else if (code == STOP) + { + break; + } + else if (was_nan) + { + if (code==CURVE3) i++; + else if (code==CURVE4) i+=2; + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==MOVETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextMoveToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==LINETO) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + CGContextAddLineToPoint(cr, point.x, point.y); + was_nan = false; + } + } + else if (code==CURVE3) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); + was_nan = false; + } + } + else if (code==CURVE4) + { + x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); + y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); + i++; + if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) + { + was_nan = true; + } + else + { + point.x = x1; + point.y = y1; + point = CGPointApplyAffineTransform(point, affine); + x1 = point.x; + y1 = point.y; + point.x = x2; + point.y = y2; + point = CGPointApplyAffineTransform(point, affine); + x2 = point.x; + y2 = point.y; + point.x = x3; + point.y = y3; + point = CGPointApplyAffineTransform(point, affine); + x3 = point.x; + y3 = point.y; + CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); + was_nan = false; + } + } + } + } + + Py_DECREF(coordinates); + Py_XDECREF(codelist); + return n; +} + +static void _draw_hatch (void *info, CGContextRef cr) +{ + PyObject* hatchpath = (PyObject*)info; + CGAffineTransform affine = CGAffineTransformMakeScale(HATCH_SIZE, HATCH_SIZE); + + int n = _draw_path(cr, hatchpath, affine); + if (n < 0) + { + PyGILState_STATE gstate = PyGILState_Ensure(); + PyErr_Print(); + PyGILState_Release(gstate); + return; + } + else if (n==0) + { + return; + } + else + { + CGContextSetLineWidth(cr, 1.0); + CGContextSetLineCap(cr, kCGLineCapSquare); + CGContextDrawPath(cr, kCGPathFillStroke); + } +} + +static void _release_hatch(void* info) +{ + PyObject* hatchpath = (PyObject*)info; + Py_DECREF(hatchpath); +} + /* ---------------------------- Cocoa classes ---------------------------- */ @@ -274,7 +541,6 @@ typedef struct { PyObject_HEAD CGContextRef cr; - CGPatternRef pattern; /* For drawing hatches */ } GraphicsContext; static PyObject* @@ -283,7 +549,6 @@ GraphicsContext* self = (GraphicsContext*)type->tp_alloc(type, 0); if (!self) return NULL; self->cr = NULL; - self->pattern = NULL; if (ngc==0) { @@ -301,8 +566,6 @@ static void GraphicsContext_dealloc(GraphicsContext *self) { - CGPatternRelease(self->pattern); - ngc--; if (ngc==0) _dealloc_atsui(); @@ -325,12 +588,6 @@ return NULL; } - if (self->pattern) - { - CGPatternRelease(self->pattern); - self->pattern = NULL; - } - CGContextRestoreGState(cr); CGContextSaveGState(cr); Py_INCREF(Py_None); @@ -431,10 +688,6 @@ PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); return NULL; } -#ifdef BUH - CGContextRestoreGState(cr); /* FIXME */ - CGContextSaveGState(cr); /* FIXME */ -#endif PyObject* path; @@ -759,116 +1012,7 @@ return Py_None; } -static void _draw_hatch (void *info, CGContextRef cr) -{ - int i; - - PyObject* string = (PyObject*)info; - char* hatches = PyString_AS_STRING(string); - - int frequency[4] = {0, 0, 0, 0}; - float position, distance; - - const float size = 12.0; - const int n = strlen(hatches); - - for (i = 0; i < n; i++) - { - switch(hatches[i]) - { - case '/': frequency[3]++; break; - case '\\': frequency[2]++; break; - case '|': frequency[1]++; break; - case '-': frequency[0]++; break; - case '+': frequency[0]++; frequency[1]++; break; - case 'x': frequency[2]++; frequency[3]++; break; - } - } - - distance = size / frequency[0]; - position = distance / 2.0; - for (i = 0; i < frequency[0]; i++, position += distance) - { - CGContextMoveToPoint(cr, 0.0, position); - CGContextAddLineToPoint(cr, size, position); - } - distance = size / frequency[1]; - position = distance / 2.0; - for (i = 0; i < frequency[1]; i++, position += distance) - { - CGContextMoveToPoint(cr, position, 0.0); - CGContextAddLineToPoint(cr, position, size); - } - distance = size / frequency[2]; - position = distance / 2.0; - for (i = 0; i < frequency[2]; i++, position += distance) - { - CGContextMoveToPoint(cr, position, 0.0); - CGContextAddLineToPoint(cr, 0.0, position); - CGContextMoveToPoint(cr, position, size); - CGContextAddLineToPoint(cr, size, position); - } - distance = size / frequency[3]; - position = distance / 2.0; - for (i = 0; i < frequency[3]; i++, position += distance) - { - CGContextMoveToPoint(cr, position, 0.0); - CGContextAddLineToPoint(cr, size, size-position); - CGContextMoveToPoint(cr, position, size); - CGContextAddLineToPoint(cr, 0.0, size-position); - } - CGContextSetLineWidth(cr, 2.0); - CGContextSetLineCap(cr, kCGLineCapSquare); - CGContextStrokePath(cr); - - Py_DECREF(string); -} - -static void _release_hatch(void* info) -{ - PyObject* hatches = info; - Py_DECREF(hatches); -} - static PyObject* -GraphicsContext_set_hatch(GraphicsContext* self, PyObject* args) -{ PyObject* hatches; - - const float size = 12.0; - static const CGPatternCallbacks callbacks = {0, - &_draw_hatch, - &_release_hatch}; - - CGContextRef cr = self->cr; - if (!cr) - { - PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); - return NULL; - } - - if(!PyArg_ParseTuple(args, "O", &hatches)) return NULL; - if(!PyString_Check(hatches)) return NULL; - - Py_INCREF(hatches); - - CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB(); - CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(baseSpace); - CGColorSpaceRelease(baseSpace); - CGContextSetFillColorSpace(cr, patternSpace); - CGColorSpaceRelease(patternSpace); - - self->pattern = CGPatternCreate((void*)hatches, - CGRectMake(0, 0, size, size), - CGAffineTransformIdentity, size, size, - kCGPatternTilingNoDistortion, - false, - &callbacks); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject* GraphicsContext_set_linewidth (GraphicsContext* self, PyObject* args) { float width; @@ -965,238 +1109,6 @@ return 1; } -static int -_draw_path(CGContextRef cr, PyObject* path, CGAffineTransform affine) -{ - CGPoint point; - - PyObject* vertices = PyObject_GetAttrString(path, "vertices"); - if (vertices==NULL) - { - PyErr_SetString(PyExc_AttributeError, "path has no vertices"); - return -1; - } - Py_DECREF(vertices); /* Don't keep a reference here */ - - PyObject* codes = PyObject_GetAttrString(path, "codes"); - if (codes==NULL) - { - PyErr_SetString(PyExc_AttributeError, "path has no codes"); - return -1; - } - Py_DECREF(codes); /* Don't keep a reference here */ - - PyArrayObject* coordinates; - coordinates = (PyArrayObject*)PyArray_FromObject(vertices, - NPY_DOUBLE, 2, 2); - if (!coordinates) - { - PyErr_SetString(PyExc_ValueError, "failed to convert vertices array"); - return -1; - } - - if (PyArray_NDIM(coordinates) != 2 || PyArray_DIM(coordinates, 1) != 2) - { - Py_DECREF(coordinates); - PyErr_SetString(PyExc_ValueError, "invalid vertices array"); - return -1; - } - - npy_intp n = PyArray_DIM(coordinates, 0); - - if (n==0) /* Nothing to do here */ - { - Py_DECREF(coordinates); - return 0; - } - - PyArrayObject* codelist = NULL; - if (codes != Py_None) - { - codelist = (PyArrayObject*)PyArray_FromObject(codes, - NPY_UINT8, 1, 1); - if (!codelist) - { - Py_DECREF(coordinates); - PyErr_SetString(PyExc_ValueError, "invalid codes array"); - return -1; - } - } - - if (codelist==NULL) - { - npy_intp i; - npy_uint8 code = MOVETO; - for (i = 0; i < n; i++) - { - point.x = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 0)); - point.y = (CGFloat)(*(double*)PyArray_GETPTR2(coordinates, i, 1)); - if (isnan(point.x) || isnan(point.y)) - { - code = MOVETO; - } - else - { - point = CGPointApplyAffineTransform(point, affine); - switch (code) - { - case MOVETO: - CGContextMoveToPoint(cr, point.x, point.y); - break; - case LINETO: - CGContextAddLineToPoint(cr, point.x, point.y); - break; - } - code = LINETO; - } - } - } - else - { - npy_intp i = 0; - BOOL was_nan = false; - npy_uint8 code; - CGFloat x1, y1, x2, y2, x3, y3; - while (i < n) - { - code = *(npy_uint8*)PyArray_GETPTR1(codelist, i); - if (code == CLOSEPOLY) - { - CGContextClosePath(cr); - i++; - } - else if (code == STOP) - { - break; - } - else if (was_nan) - { - if (code==CURVE3) i++; - else if (code==CURVE4) i+=2; - x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - if (isnan(x1) || isnan(y1)) - { - was_nan = true; - } - else - { - point.x = x1; - point.y = y1; - point = CGPointApplyAffineTransform(point, affine); - CGContextMoveToPoint(cr, point.x, point.y); - was_nan = false; - } - } - else if (code==MOVETO) - { - x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - if (isnan(x1) || isnan(y1)) - { - was_nan = true; - } - else - { - point.x = x1; - point.y = y1; - point = CGPointApplyAffineTransform(point, affine); - CGContextMoveToPoint(cr, point.x, point.y); - was_nan = false; - } - } - else if (code==LINETO) - { - x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - if (isnan(x1) || isnan(y1)) - { - was_nan = true; - } - else - { - point.x = x1; - point.y = y1; - point = CGPointApplyAffineTransform(point, affine); - CGContextAddLineToPoint(cr, point.x, point.y); - was_nan = false; - } - } - else if (code==CURVE3) - { - x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2)) - { - was_nan = true; - } - else - { - point.x = x1; - point.y = y1; - point = CGPointApplyAffineTransform(point, affine); - x1 = point.x; - y1 = point.y; - point.x = x2; - point.y = y2; - point = CGPointApplyAffineTransform(point, affine); - x2 = point.x; - y2 = point.y; - CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2); - was_nan = false; - } - } - else if (code==CURVE4) - { - x1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y1 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - x2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y2 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - x3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 0)); - y3 = (CGFloat) (*(double*)PyArray_GETPTR2(coordinates, i, 1)); - i++; - if (isnan(x1) || isnan(y1) || isnan(x2) || isnan(y2) || isnan(x3) || isnan(y3)) - { - was_nan = true; - } - else - { - point.x = x1; - point.y = y1; - point = CGPointApplyAffineTransform(point, affine); - x1 = point.x; - y1 = point.y; - point.x = x2; - point.y = y2; - point = CGPointApplyAffineTransform(point, affine); - x2 = point.x; - y2 = point.y; - point.x = x3; - point.y = y3; - point = CGPointApplyAffineTransform(point, affine); - x3 = point.x; - y3 = point.y; - CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3); - was_nan = false; - } - } - } - } - - Py_DECREF(coordinates); - Py_XDECREF(codelist); - return n; -} - static PyObject* GraphicsContext_draw_path (GraphicsContext* self, PyObject* args) { @@ -1230,6 +1142,7 @@ if (n > 0) { + PyObject* hatchpath; if(rgbFace) { float r, g, b; @@ -1239,22 +1152,60 @@ return NULL; } CGContextSaveGState(cr); - if(self->pattern) - { - float components[4]; - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = 1.0; - CGContextSetFillPattern(cr, self->pattern, components); - CGPatternRelease(self->pattern); - self->pattern = nil; - } - else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + CGContextSetRGBFillColor(cr, r, g, b, 1.0); CGContextDrawPath(cr, kCGPathFillStroke); CGContextRestoreGState(cr); } else CGContextStrokePath(cr); + + hatchpath = PyObject_CallMethod((PyObject*)self, "get_hatch_path", ""); + if (!hatchpath) + { + return NULL; + } + else if (hatchpath==Py_None) + { + Py_DECREF(hatchpath); + } + else + { + float color[4] = {0, 0, 0, 1}; + CGPatternRef pattern; + static const CGPatternCallbacks callbacks = {0, + &_draw_hatch, + &_release_hatch}; + PyObject* rgb = PyObject_CallMethod((PyObject*)self, "get_rgb", ""); + if (!rgb) + { + Py_DECREF(hatchpath); + return NULL; + } + ok = PyArg_ParseTuple(rgb, "ffff", &color[0], &color[1], &color[2], &color[3]); + Py_DECREF(rgb); + if (!ok) + { + Py_DECREF(hatchpath); + return NULL; + } + + CGColorSpaceRef baseSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(baseSpace); + CGColorSpaceRelease(baseSpace); + CGContextSetFillColorSpace(cr, patternSpace); + CGColorSpaceRelease(patternSpace); + + pattern = CGPatternCreate((void*)hatchpath, + CGRectMake(0, 0, HATCH_SIZE, HATCH_SIZE), + CGAffineTransformIdentity, + HATCH_SIZE, HATCH_SIZE, + kCGPatternTilingNoDistortion, + false, + &callbacks); + CGContextSetFillPattern(cr, pattern, color); + CGPatternRelease(pattern); + _draw_path(cr, path, affine); + CGContextFillPath(cr); + } } Py_INCREF(Py_None); @@ -1297,18 +1248,7 @@ { return NULL; } - if(self->pattern) - { - float components[4]; - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = 1.0; - CGContextSetFillPattern(cr, self->pattern, components); - CGPatternRelease(self->pattern); - self->pattern = nil; - } - else CGContextSetRGBFillColor(cr, r, g, b, 1.0); + CGContextSetRGBFillColor(cr, r, g, b, 1.0); } CGAffineTransform affine; @@ -2516,7 +2456,7 @@ const size_t nComponents = 4; /* red, green, blue, alpha */ const size_t bitsPerPixel = bitsPerComponent * nComponents; const size_t bytesPerRow = nComponents * bytesPerComponent * ncols; - CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); + CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); Py_INCREF(image); n = PyString_GET_SIZE(image); @@ -2627,22 +2567,6 @@ METH_VARARGS, "Sets the current stroke and fill color to a value in the DeviceGray color space." }, - {"set_hatch", - (PyCFunction)GraphicsContext_set_hatch, - METH_VARARGS, - "\n" - " hatch can be one of:\n" - " / - diagonal hatching\n" - " \\ - back diagonal\n" - " | - vertical\n" - " - - horizontal\n" - " # - crossed\n" - " X - crossed diagonal\n" - " letters can be combined, in which case all the specified\n" - " hatchings are done\n" - " if same letter repeats, it increases the density of hatching\n" - " in that direction\n" - }, {"set_linewidth", (PyCFunction)GraphicsContext_set_linewidth, METH_VARARGS, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2009-01-10 20:05:26
|
Revision: 6772 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6772&view=rev Author: jdh2358 Date: 2009-01-10 20:05:24 +0000 (Sat, 10 Jan 2009) Log Message: ----------- michiel's backend qt draw idle patch - sf id 2468809 Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-01-10 20:03:59 UTC (rev 6771) +++ trunk/matplotlib/CHANGELOG 2009-01-10 20:05:24 UTC (rev 6772) @@ -1,5 +1,6 @@ -2009-01-10 Applied Michiel's hatch patch for macosx backend. Closes - sf patch 2497785 - JDH +2009-01-10 Applied Michiel's hatch patch for macosx backend and + draw_idle patch for qt. Closes sf patched 2497785 and + 2468809 - JDH 2009-01-06 Fix bug in setting of dashed negative contours. - EF Modified: trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py 2009-01-10 20:03:59 UTC (rev 6771) +++ trunk/matplotlib/lib/matplotlib/backends/backend_qt4.py 2009-01-10 20:05:24 UTC (rev 6772) @@ -37,7 +37,7 @@ if matplotlib.is_interactive(): figManager = Gcf.get_active() if figManager != None: - figManager.canvas.draw() + figManager.canvas.draw_idle() def _create_qApp(): """ @@ -97,6 +97,7 @@ FigureCanvasBase.__init__( self, figure ) self.figure = figure self.setMouseTracking( True ) + self._idle = True # hide until we can test and fix #self.startTimer(backend_IdleEvent.milliseconds) w,h = self.get_width_height() @@ -198,6 +199,15 @@ FigureCanvasBase.stop_event_loop_default(self) stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__ + def draw_idle(self): + 'update drawing area only if idle' + d = self._idle + self._idle = False + def idle_draw(*args): + self.draw() + self._idle = True + if d: QtCore.QTimer.singleShot(0, idle_draw) + class FigureManagerQT( FigureManagerBase ): """ Public attributes This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |