From: <md...@us...> - 2008-10-23 15:30:08
|
Revision: 6306 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6306&view=rev Author: mdboom Date: 2008-10-23 15:29:50 +0000 (Thu, 23 Oct 2008) Log Message: ----------- Fix doc markup. Modified Paths: -------------- trunk/matplotlib/doc/faq/howto_faq.rst trunk/matplotlib/lib/matplotlib/ticker.py Modified: trunk/matplotlib/doc/faq/howto_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/howto_faq.rst 2008-10-23 13:30:15 UTC (rev 6305) +++ trunk/matplotlib/doc/faq/howto_faq.rst 2008-10-23 15:29:50 UTC (rev 6306) @@ -542,7 +542,7 @@ Write a tutorial on the signal analysis plotting functions like :func:`~matplotlib.pyplot.xcorr`, :func:`~matplotlib.pyplot.psd` and :func:`~matplotlib.pyplot.specgram`. Do you use matplotlib with -`django <http://www.djangoproject.com>`_ or other popular web +`django <http://www.djangoproject.com/>`_ or other popular web application servers? Write a FAQ or tutorial and we'll find a place for it in the :ref:`users-guide-index`. Bundle matplotlib in a `py2exe <http://www.py2exe.org/>`_ app? ... I think you get the idea. Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2008-10-23 13:30:15 UTC (rev 6305) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2008-10-23 15:29:50 UTC (rev 6306) @@ -77,22 +77,22 @@ formatter operates on a single tick value and returns a string to the axis. -:clss:`NullFormatter` +:class:`NullFormatter` no labels on the ticks -:clss:`FixedFormatter` +:class:`FixedFormatter` set the strings manually for the labels -:clss:`FuncFormatter` +:class:`FuncFormatter` user defined function sets the labels -:clss:`FormatStrFormatter` +:class:`FormatStrFormatter` use a sprintf format string -:clss:`ScalarFormatter` +:class:`ScalarFormatter` default formatter for scalars; autopick the fmt string -:clss:`LogFormatter` +:class:`LogFormatter` formatter for log axes @@ -109,7 +109,7 @@ ax.yaxis.set_minor_formatter( yminorFormatter ) See :ref:`pylab_examples-major_minor_demo1` for an example of setting -major an minor ticks. See the :module:`matplotlib.dates` module for +major an minor ticks. See the :mod:`matplotlib.dates` module for more information and examples of using date locators and formatters. """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2008-10-23 16:38:15
|
Revision: 6310 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6310&view=rev Author: jouni Date: 2008-10-23 16:38:04 +0000 (Thu, 23 Oct 2008) Log Message: ----------- Fixed exception in dviread that happened with Minion Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/dviread.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-10-23 16:10:59 UTC (rev 6309) +++ trunk/matplotlib/CHANGELOG 2008-10-23 16:38:04 UTC (rev 6310) @@ -1,3 +1,5 @@ +2008-10-23 Fixed exception in dviread that happened with Minion - JKS + 2008-10-21 set_xlim, ylim now return a copy of the viewlim array to avoid modify inplace surprises Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2008-10-23 16:10:59 UTC (rev 6309) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2008-10-23 16:38:04 UTC (rev 6310) @@ -394,8 +394,12 @@ self._scale, self._tfm, self.texname, self._vf = \ scale, tfm, texname, vf self.size = scale * (72.0 / (72.27 * 2**16)) + try: + nchars = max(tfm.width.iterkeys()) + except ValueError: + nchars = 0 self.widths = [ (1000*tfm.width.get(char, 0)) >> 20 - for char in range(0, max(tfm.width)) ] + for char in range(nchars) ] def __eq__(self, other): return self.__class__ == other.__class__ and \ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-10-24 00:09:02
|
Revision: 6315 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6315&view=rev Author: efiring Date: 2008-10-24 00:08:58 +0000 (Fri, 24 Oct 2008) Log Message: ----------- Support autoscaling with shared axes Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/ticker.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-10-23 19:56:04 UTC (rev 6314) +++ trunk/matplotlib/CHANGELOG 2008-10-24 00:08:58 UTC (rev 6315) @@ -1,3 +1,5 @@ +2008-10-23 Autoscaling is now supported with shared axes - EF + 2008-10-23 Fixed exception in dviread that happened with Minion - JKS 2008-10-21 set_xlim, ylim now return a copy of the viewlim array to Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-10-23 19:56:04 UTC (rev 6314) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-10-24 00:08:58 UTC (rev 6315) @@ -1451,20 +1451,30 @@ """ # if image data only just use the datalim if not self._autoscaleon: return + if scalex: + xshared = self._shared_x_axes.get_siblings(self) + dl = [ax.dataLim for ax in xshared] + bb = mtransforms.BboxBase.union(dl) + x0, x1 = bb.intervalx + if scaley: + yshared = self._shared_y_axes.get_siblings(self) + dl = [ax.dataLim for ax in yshared] + bb = mtransforms.BboxBase.union(dl) + y0, y1 = bb.intervaly if (tight or (len(self.images)>0 and len(self.lines)==0 and len(self.patches)==0)): - - if scalex: self.set_xbound(self.dataLim.intervalx) - - if scaley: self.set_ybound(self.dataLim.intervaly) + if scalex: + self.set_xbound(x0, x1) + if scaley: + self.set_ybound(y0, 11) return if scalex: - XL = self.xaxis.get_major_locator().autoscale() + XL = self.xaxis.get_major_locator().view_limits(x0, x1) self.set_xbound(XL) if scaley: - YL = self.yaxis.get_major_locator().autoscale() + YL = self.yaxis.get_major_locator().view_limits(y0, y1) self.set_ybound(YL) #### Drawing Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2008-10-23 19:56:04 UTC (rev 6314) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2008-10-24 00:08:58 UTC (rev 6315) @@ -641,9 +641,17 @@ 'Return the locations of the ticks' raise NotImplementedError('Derived must override') + def view_limits(self, vmin, vmax): + """ + select a scale for the range from vmin to vmax + + Normally This will be overridden. + """ + return mtransforms.nonsingular(vmin, vmax) + def autoscale(self): 'autoscale the view limits' - return mtransforms.nonsingular(*self.axis.get_view_interval()) + return self.view_limits(*self.axis.get_view_interval()) def pan(self, numsteps): 'Pan numticks (can be positive or negative)' @@ -773,11 +781,9 @@ def _set_numticks(self): self.numticks = 11 # todo; be smart here; this is just for dev - def autoscale(self): + def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' - vmin, vmax = self.axis.get_data_interval() - if vmax<vmin: vmin, vmax = vmax, vmin @@ -859,13 +865,11 @@ locs = vmin + np.arange(n+1) * base return locs - def autoscale(self): + def view_limits(self, dmin, dmax): """ Set the view limits to the nearest multiples of base that contain the data """ - dmin, dmax = self.axis.get_data_interval() - vmin = self._base.le(dmin) vmax = self._base.ge(dmax) if vmin==vmax: @@ -946,8 +950,7 @@ vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander = 0.05) return self.bin_boundaries(vmin, vmax) - def autoscale(self): - dmin, dmax = self.axis.get_data_interval() + def view_limits(self, dmin, dmax): if self._symmetric: maxabs = max(abs(dmin), abs(dmax)) dmin = -maxabs @@ -1043,9 +1046,9 @@ return np.array(ticklocs) - def autoscale(self): + def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' - vmin, vmax = self.axis.get_data_interval() + if vmax<vmin: vmin, vmax = vmax, vmin @@ -1114,10 +1117,9 @@ ticklocs = np.sign(decades) * b ** np.abs(decades) return np.array(ticklocs) - def autoscale(self): + def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' b = self._transform.base - vmin, vmax = self.axis.get_data_interval() if vmax<vmin: vmin, vmax = vmax, vmin @@ -1167,13 +1169,12 @@ d = abs(vmax-vmin) self._locator = self.get_locator(d) - def autoscale(self): + def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' - vmin, vmax = self.axis.get_data_interval() d = abs(vmax-vmin) self._locator = self.get_locator(d) - return self._locator.autoscale() + return self._locator.view_limits(vmin, vmax) def get_locator(self, d): 'pick the best locator based on a distance' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-24 14:05:13
|
Revision: 6318 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6318&view=rev Author: mdboom Date: 2008-10-24 14:05:07 +0000 (Fri, 24 Oct 2008) Log Message: ----------- Support reading low-bit depth (< 8) PNG images. Add PNG test suite to make sure we're supporting the basic kinds of PNG files available. Modified Paths: -------------- trunk/matplotlib/src/_png.cpp Added Paths: ----------- trunk/matplotlib/examples/tests/pngsuite/ trunk/matplotlib/examples/tests/pngsuite/basn0g01.png trunk/matplotlib/examples/tests/pngsuite/basn0g02.png trunk/matplotlib/examples/tests/pngsuite/basn0g04.png trunk/matplotlib/examples/tests/pngsuite/basn0g08.png trunk/matplotlib/examples/tests/pngsuite/basn0g16.png trunk/matplotlib/examples/tests/pngsuite/basn2c08.png trunk/matplotlib/examples/tests/pngsuite/basn2c16.png trunk/matplotlib/examples/tests/pngsuite/basn3p01.png trunk/matplotlib/examples/tests/pngsuite/basn3p02.png trunk/matplotlib/examples/tests/pngsuite/basn3p04.png trunk/matplotlib/examples/tests/pngsuite/basn3p08.png trunk/matplotlib/examples/tests/pngsuite/basn4a08.png trunk/matplotlib/examples/tests/pngsuite/basn4a16.png trunk/matplotlib/examples/tests/pngsuite/basn6a08.png trunk/matplotlib/examples/tests/pngsuite/basn6a16.png trunk/matplotlib/examples/tests/pngsuite/pngsuite.py trunk/matplotlib/examples/tests/pngsuite/truth.png Added: trunk/matplotlib/examples/tests/pngsuite/basn0g01.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn0g01.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn0g02.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn0g02.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn0g04.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn0g04.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn0g08.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn0g08.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn0g16.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn0g16.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn2c08.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn2c08.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn2c16.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn2c16.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn3p01.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn3p01.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn3p02.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn3p02.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn3p04.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn3p04.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn3p08.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn3p08.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn4a08.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn4a08.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn4a16.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn4a16.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn6a08.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn6a08.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/basn6a16.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/basn6a16.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/matplotlib/examples/tests/pngsuite/pngsuite.py =================================================================== --- trunk/matplotlib/examples/tests/pngsuite/pngsuite.py (rev 0) +++ trunk/matplotlib/examples/tests/pngsuite/pngsuite.py 2008-10-24 14:05:07 UTC (rev 6318) @@ -0,0 +1,24 @@ +""" +This test loads a subset of the files in Willem van Schaik's PNG test +suite available here: + + http://libpng.org/pub/png/pngsuite.html + +The result should look like truth.png. +""" + +from matplotlib import pyplot as plt +import glob + +files = glob.glob("basn*.png") +files.sort() + +plt.figure(figsize=(len(files), 2)) + +for i, fname in enumerate(files): + data = plt.imread(fname) + plt.imshow(data, extent=[i,i+1,0,1]) + +plt.gca().get_frame().set_facecolor("#ddffff") +plt.gca().set_xlim(0, len(files)) +plt.show() Added: trunk/matplotlib/examples/tests/pngsuite/truth.png =================================================================== (Binary files differ) Property changes on: trunk/matplotlib/examples/tests/pngsuite/truth.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: trunk/matplotlib/src/_png.cpp =================================================================== --- trunk/matplotlib/src/_png.cpp 2008-10-24 11:43:31 UTC (rev 6317) +++ trunk/matplotlib/src/_png.cpp 2008-10-24 14:05:07 UTC (rev 6318) @@ -213,19 +213,25 @@ png_uint_32 width = info_ptr->width; png_uint_32 height = info_ptr->height; + bool do_gray_conversion = (info_ptr->bit_depth < 8 && + info_ptr->color_type == PNG_COLOR_TYPE_GRAY); + int bit_depth = info_ptr->bit_depth; + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } else if (bit_depth < 8) { + png_set_packing(png_ptr); + } else { + } + // convert misc color types to rgb for simplicity if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY || - info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); - else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); + } - - int bit_depth = info_ptr->bit_depth; - if (bit_depth == 16) png_set_strip_16(png_ptr); - - png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); @@ -247,8 +253,6 @@ png_read_image(png_ptr, row_pointers); - - npy_intp dimensions[3]; dimensions[0] = height; //numrows dimensions[1] = width; //numcols @@ -256,19 +260,31 @@ PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(3, dimensions, PyArray_FLOAT); - - for (png_uint_32 y = 0; y < height; y++) { - png_byte* row = row_pointers[y]; - for (png_uint_32 x = 0; x < width; x++) { - - png_byte* ptr = (rgba) ? &(row[x*4]) : &(row[x*3]); - size_t offset = y*A->strides[0] + x*A->strides[1]; - //if ((y<10)&&(x==10)) std::cout << "r = " << ptr[0] << " " << ptr[0]/255.0 << std::endl; - *(float*)(A->data + offset + 0*A->strides[2]) = (float)(ptr[0]/255.0f); - *(float*)(A->data + offset + 1*A->strides[2]) = (float)(ptr[1]/255.0f); - *(float*)(A->data + offset + 2*A->strides[2]) = (float)(ptr[2]/255.0f); - *(float*)(A->data + offset + 3*A->strides[2]) = rgba ? (float)(ptr[3]/255.0f) : 1.0f; + if (do_gray_conversion) { + float max_value = (float)((1L << bit_depth) - 1); + for (png_uint_32 y = 0; y < height; y++) { + png_byte* row = row_pointers[y]; + for (png_uint_32 x = 0; x < width; x++) { + float value = row[x] / max_value; + size_t offset = y*A->strides[0] + x*A->strides[1]; + *(float*)(A->data + offset + 0*A->strides[2]) = value; + *(float*)(A->data + offset + 1*A->strides[2]) = value; + *(float*)(A->data + offset + 2*A->strides[2]) = value; + *(float*)(A->data + offset + 3*A->strides[2]) = 1.0f; + } } + } else { + for (png_uint_32 y = 0; y < height; y++) { + png_byte* row = row_pointers[y]; + for (png_uint_32 x = 0; x < width; x++) { + png_byte* ptr = (rgba) ? &(row[x*4]) : &(row[x*3]); + size_t offset = y*A->strides[0] + x*A->strides[1]; + *(float*)(A->data + offset + 0*A->strides[2]) = (float)(ptr[0]/255.0); + *(float*)(A->data + offset + 1*A->strides[2]) = (float)(ptr[1]/255.0); + *(float*)(A->data + offset + 2*A->strides[2]) = (float)(ptr[2]/255.0); + *(float*)(A->data + offset + 3*A->strides[2]) = rgba ? (float)(ptr[3]/255.0) : 1.0f; + } + } } //free the png memory This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-24 17:12:29
|
Revision: 6322 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6322&view=rev Author: mdboom Date: 2008-10-24 17:12:24 +0000 (Fri, 24 Oct 2008) Log Message: ----------- Add mathtext API docs. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/mathtext.py Added Paths: ----------- trunk/matplotlib/doc/api/mathtext_api.rst Added: trunk/matplotlib/doc/api/mathtext_api.rst =================================================================== --- trunk/matplotlib/doc/api/mathtext_api.rst (rev 0) +++ trunk/matplotlib/doc/api/mathtext_api.rst 2008-10-24 17:12:24 UTC (rev 6322) @@ -0,0 +1,14 @@ +******************* +matplotlib mathtext +******************* + +.. inheritance-diagram:: matplotlib.mathtext + :parts: 1 + +:mod:`matplotlib.mathtext` +============================= + +.. automodule:: matplotlib.mathtext + :members: + :undoc-members: + :show-inheritance: Modified: trunk/matplotlib/lib/matplotlib/mathtext.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mathtext.py 2008-10-24 15:53:59 UTC (rev 6321) +++ trunk/matplotlib/lib/matplotlib/mathtext.py 2008-10-24 17:12:24 UTC (rev 6322) @@ -1,175 +1,21 @@ r""" +:mod:`~matplotlib.mathtext` is a module for parsing a subset of the +TeX math syntax and drawing them to a matplotlib backend. -OVERVIEW +For a tutorial of its usage see :ref:`mathtext-tutorial`. This +document is primarily concerned with implementation details. - mathtext is a module for parsing TeX expressions and drawing them - into a matplotlib.ft2font image buffer. You can draw from this - buffer into your backend. +The module uses pyparsing_ to parse the TeX expression. - A large set of the TeX symbols are provided (see below). - Subscripting and superscripting are supported, as well as the - over/under style of subscripting with \sum, \int, etc. +.. _pyparsing: http://pyparsing.wikispaces.com/ - The module uses pyparsing to parse the TeX expression, an so can - handle fairly complex TeX expressions Eg, the following renders - correctly +The Bakoma distribution of the TeX Computer Modern fonts, and STIX +fonts are supported. There is experimental support for using +arbitrary fonts, but results may vary without proper tweaking and +metrics for those fonts. - s = r'$\mathcal{R}\prod_{i=\alpha\mathcal{B}}^\infty a_i\sin(2 \pi f x_i)$' - - Different fonts may be selected: - \mathcal Calligraphic fonts - \mathrm Roman (upright) font - \mathit Italic font - \mathtt Typewriter (monospaced) font, similar to Courier - - Additionally, if using the STIX fonts: - \mathbb Blackboard (double-struck) font - \mathcircled Circled characters - \mathfrak Fraktur (Gothic-style) font - \mathscr Script (cursive) font - \mathsf Sans-serif font - - The following accents are provided: \hat, \breve, \grave, \bar, - \acute, \tilde, \vec, \dot, \ddot. All of them have the same - syntax, eg to make an overbar you do \bar{o} or to make an o umlaut - you do \ddot{o}. The shortcuts are also provided, eg: \"o \'e \`e - \~n \.x \^y - - The spacing elements \ , \/ and \hspace{num} are provided. \/ - inserts a small space, and \hspace{num} inserts a fraction of the - current fontsize. Eg, if num=0.5 and the fontsize is 12.0, - hspace{0.5} inserts 6 points of space - - - - If you find TeX expressions that don't parse or render properly, - please email me, but please check KNOWN ISSUES below first. - -REQUIREMENTS - - mathtext requires matplotlib.ft2font. Set BUILD_FT2FONT=True in - setup.py. See BACKENDS below for a summary of availability by - backend. - -LICENSING: - - The computer modern fonts this package uses are part of the BaKoMa - fonts, which are (now) free for commercial and noncommercial use and - redistribution; see license/LICENSE_BAKOMA in the matplotlib src - distribution for redistribution requirements. - -USAGE: - - See http://matplotlib.sf.net/users/mathtext.html for a tutorial - introduction. - - Any text element (xlabel, ylabel, title, text, etc) can use TeX - markup, as in - - xlabel(r'$\Delta_i$') - ^ - use raw strings - - Math and non-math can be interpresed in the same string. E.g., - - r'My label $x_i$'. - - A large set of the TeX symbols are provided. Subscripting and - superscripting are supported, as well as the over/under style of - subscripting with \sum, \int, etc. - - - Allowed TeX symbols: - - $ \% \AA \AE \BbbC \BbbN \BbbP \BbbQ \BbbR \BbbZ \Bumpeq \Cap \Colon - \Cup \Delta \Doteq \Downarrow \Equiv \Finv \Gamma \H \Im \L \Lambda - \Ldsh \Leftarrow \Leftrightarrow \Lleftarrow \Lsh \Nearrow \Nwarrow - \O \OE \Omega \P \Phi \Pi \Psi \Rdsh \Re \Rightarrow \Rrightarrow - \Rsh \S \Searrow \Sigma \Subset \Supset \Swarrow \Theta \Uparrow - \Updownarrow \Upsilon \Vdash \Vert \Vvdash \Xi \_ \__sqrt__ \ac - \acute \acwopencirclearrow \adots \ae \aleph \alpha \angle \approx - \approxeq \approxident \arceq \ast \asymp \backcong \backprime - \backsim \backsimeq \backslash \bar \barleftarrow \barwedge \because - \beta \beth \between \bigcap \bigcirc \bigcup \bigodot \bigoplus - \bigotimes \bigstar \bigtriangledown \bigtriangleup \biguplus - \bigvee \bigwedge \blacksquare \blacktriangle \blacktriangledown - \blacktriangleleft \blacktriangleright \bot \bowtie \boxbar \boxdot - \boxminus \boxplus \boxtimes \breve \bullet \bumpeq \c \candra \cap - \carriagereturn \cdot \cdotp \cdots \check \checkmark \chi \circ - \circeq \circledR \circledS \circledast \circledcirc \circleddash - \circumflexaccent \clubsuit \clubsuitopen \colon \coloneq - \combiningacuteaccent \combiningbreve \combiningdiaeresis - \combiningdotabove \combininggraveaccent \combiningoverline - \combiningrightarrowabove \combiningtilde \complement \cong \coprod - \copyright \cup \cupdot \curlyeqprec \curlyeqsucc \curlyvee - \curlywedge \curvearrowleft \curvearrowright \cwopencirclearrow \d - \dag \daleth \danger \dashv \ddag \ddddot \dddot \ddot \ddots - \degree \delta \diamond \diamondsuit \digamma \div \divideontimes - \dot \doteq \dotminus \dotplus \dots \doublebarwedge ? \downarrow - \downdownarrows \downharpoonleft \downharpoonright \downzigzagarrow - \ell \emdash \emptyset \endash \enspace \epsilon \eqcirc \eqcolon - \eqdef \eqgtr \eqless \eqsim \equiv \eta \eth \exists \fallingdotseq - \flat \forall \frakC \frakZ \frown \gamma \geq \geqq \gg \ggg \gimel - \gneqq \gnsim \grave \greater \gtrdot \gtreqless \gtrless \gtrsim - \hat \heartsuit \hookleftarrow \hookrightarrow \i \iiint \iint - \imageof \imath \in \infty \int \intercal \invnot \iota \jmath \k - \kappa \kernelcontraction \l \lambda \lambdabar \lasp \lbrace - \lbrack \lceil \leftangle \leftarrow \leftarrowtail \leftbrace - \leftharpoonaccent \leftharpoondown \leftharpoonup \leftleftarrows - \leftparen \leftrightarrow \leftrightarrows \leftrightharpoons - \leftthreetimes \leq \leqq \less \lessdot \lesseqgtr \lessgtr - \lesssim \lfloor \ll \llcorner \lll \lneqq \lnsim \looparrowleft - \looparrowright \lq \lrcorner \ltimes \maltese \mapsdown \mapsfrom - \mapsto \mapsup \measeq \measuredangle \mho \mid \minus \models \mp - \mu \multimap \nLeftarrow \nLeftrightarrow \nRightarrow \nVDash - \nVdash \nabla \napprox \natural \ncong \ne \nearrow \neg \nequiv - \nexists \ngeq \ngtr \ni \nleftarrow \nleftrightarrow \nleq \nless - \nmid \not \notin \nparallel \nprec \nrightarrow \nsim \nsime - \nsubset \nsubseteq \nsucc \nsupset \nsupseteq \ntriangleleft - \ntrianglelefteq \ntriangleright \ntrianglerighteq \nu \nvDash - \nvdash \nwarrow \o \obar \ocirc \odot \oe \oiiint \oiint \oint - \omega \ominus \oplus \origof \oslash \otimes \overarc - \overleftarrow \overleftrightarrow \parallel \partial \phi \pi - \pitchfork \pm \prec \preccurlyeq \preceq \precnsim \precsim \prime - \prod \propto \prurel \psi \quad \questeq \rasp \rbrace \rbrack - \rceil \rfloor \rho \rightangle \rightarrow \rightarrowbar - \rightarrowtail \rightbrace \rightharpoonaccent \rightharpoondown - \rightharpoonup \rightleftarrows \rightleftharpoons \rightparen - \rightrightarrows \rightthreetimes \rightzigzagarrow \risingdotseq - \rq \rtimes \scrB \scrE \scrF \scrH \scrI \scrL \scrM \scrR \scre - \scrg \scro \scurel \searrow \sharp \sigma \sim \simeq \slash - \smallsetminus \smile \solbar \spadesuit \spadesuitopen - \sphericalangle \sqcap \sqcup \sqsubset \sqsubseteq \sqsupset - \sqsupseteq \ss \star \stareq \sterling \subset \subseteq \subsetneq - \succ \succcurlyeq \succeq \succnsim \succsim \sum \supset \supseteq - \supsetneq \swarrow \t \tau \textasciiacute \textasciicircum - \textasciigrave \textasciitilde \textexclamdown \textquestiondown - \textquotedblleft \textquotedblright \therefore \theta \thickspace - \thinspace \tilde \times \to \top \triangledown \triangleleft - \trianglelefteq \triangleq \triangleright \trianglerighteq - \turnednot \twoheaddownarrow \twoheadleftarrow \twoheadrightarrow - \twoheaduparrow \ulcorner \underbar \uparrow \updownarrow - \updownarrowbar \updownarrows \upharpoonleft \upharpoonright \uplus - \upsilon \upuparrows \urcorner \vDash \varepsilon \varkappa - \varnothing \varphi \varpi \varrho \varsigma \vartheta \vartriangle - \vartriangleleft \vartriangleright \vdash \vdots \vec \vee \veebar - \veeeq \vert \wedge \wedgeq \widehat \widetilde \wp \wr \xi \yen - \zeta \{ \| \} - -BACKENDS - - mathtext currently works with all backends. - -KNOWN ISSUES: - - - Certainly there are some... - -Author : John Hunter <jdh...@ac...> - Michael Droettboom <md...@st...> - (rewrite based on TeX box layout algorithms) -Copyright : John Hunter (2004,2005) -License : matplotlib license (PSF compatible) - +If you find TeX expressions that don't parse or render properly, +please email md...@st..., but please check KNOWN ISSUES below first. """ from __future__ import division import os @@ -214,10 +60,9 @@ def get_unicode_index(symbol): """get_unicode_index(symbol) -> integer -Return the integer index (from the Unicode table) of symbol. -symbol can be a single unicode character, a TeX command (i.e. r'\pi'), -or a Type1 symbol name (i.e. 'phi'). - +Return the integer index (from the Unicode table) of symbol. *symbol* +can be a single unicode character, a TeX command (i.e. r'\pi'), or a +Type1 symbol name (i.e. 'phi'). """ # From UTF #25: U+2212 minus sign is the preferred # representation of the unary and binary minus sign rather than @@ -239,32 +84,62 @@ class MathtextBackend(object): + """ + The base class for the mathtext backend-specific code. The + purpose of :class:`MathtextBackend` subclasses is to interface + between mathtext and a specific matplotlib graphics backend. + + Subclasses need to override the following: + + - :meth:`render_glyph` + - :meth:`render_filled_rect` + - :meth:`get_results` + + And optionally, if you need to use a Freetype hinting style: + + - :meth:`get_hinting_type` + """ def __init__(self): self.fonts_object = None def set_canvas_size(self, w, h, d): - 'Dimension the drawing canvas; may be a noop' + 'Dimension the drawing canvas' self.width = w self.height = h self.depth = d def render_glyph(self, ox, oy, info): + """ + Draw a glyph described by *info* to the reference point (*ox*, + *oy*). + """ raise NotImplementedError() def render_filled_rect(self, x1, y1, x2, y2): + """ + Draw a filled black rectangle from (*x1*, *y1*) to (*x2*, *y2*). + """ raise NotImplementedError() def get_results(self, box): - """Return a backend specific tuple of things to return to the - backend after all processing is done.""" + """ + Return a backend-specific tuple to return to the backend after + all processing is done. + """ raise NotImplementedError() def get_hinting_type(self): + """ + Get the Freetype hinting type to use with this particular + backend. + """ return LOAD_NO_HINTING class MathtextBackendBbox(MathtextBackend): - """A backend whose only purpose is to get a precise bounding box. - Only required for the Agg backend.""" + """ + A backend whose only purpose is to get a precise bounding box. + Only required for the Agg backend. + """ def __init__(self, real_backend): MathtextBackend.__init__(self) @@ -310,6 +185,10 @@ self.real_backend.oy = self.bbox[1] class MathtextBackendAggRender(MathtextBackend): + """ + Render glyphs and rectangles to an FTImage buffer, which is later + transferred to the Agg image by the Agg backend. + """ def __init__(self): self.ox = 0 self.oy = 0 @@ -353,9 +232,17 @@ return self.image, self.depth def MathtextBackendBitmap(): + """ + A backend to generate standalone mathtext images. No additional + matplotlib backend is required. + """ return MathtextBackendBbox(MathtextBackendBitmapRender()) class MathtextBackendPs(MathtextBackend): + """ + Store information to write a mathtext rendering to the PostScript + backend. + """ def __init__(self): self.pswriter = StringIO() self.lastfont = None @@ -393,6 +280,10 @@ self.fonts_object.get_used_characters()) class MathtextBackendPdf(MathtextBackend): + """ + Store information to write a mathtext rendering to the PDF + backend. + """ def __init__(self): self.glyphs = [] self.rects = [] @@ -417,6 +308,10 @@ self.fonts_object.get_used_characters()) class MathtextBackendSvg(MathtextBackend): + """ + Store information to write a mathtext rendering to the SVG + backend. + """ def __init__(self): self.svg_glyphs = [] self.svg_rects = [] @@ -442,6 +337,11 @@ self.fonts_object.get_used_characters()) class MathtextBackendCairo(MathtextBackend): + """ + Store information to write a mathtext rendering to the Cairo + backend. + """ + def __init__(self): self.glyphs = [] self.rects = [] @@ -466,7 +366,7 @@ class Fonts(object): """ - An abstract base class for fonts that want to render mathtext + An abstract base class for a system of fonts to use for mathtext. The class must be able to take symbol keys and font file names and return the character metrics. It also delegates to a backend class @@ -474,11 +374,15 @@ """ def __init__(self, default_font_prop, mathtext_backend): - """default_font_prop: A FontProperties object to use for the - default non-math font, or the base font for Unicode font - rendering. - mathtext_backend: A subclass of MathTextBackend used to - delegate the actual rendering.""" + """ + *default_font_prop*: A + :class:`~matplotlib.font_manager.FontProperties` object to use + for the default non-math font, or the base font for Unicode + (generic) font rendering. + + *mathtext_backend*: A subclass of :class:`MathTextBackend` + used to delegate the actual rendering. + """ self.default_font_prop = default_font_prop self.mathtext_backend = mathtext_backend # Make these classes doubly-linked @@ -486,51 +390,86 @@ self.used_characters = {} def destroy(self): - """Fix any cyclical references before the object is about - to be destroyed.""" + """ + Fix any cyclical references before the object is about + to be destroyed. + """ self.used_characters = None def get_kern(self, font1, fontclass1, sym1, fontsize1, font2, fontclass2, sym2, fontsize2, dpi): """ - Get the kerning distance for font between sym1 and sym2. + Get the kerning distance for font between *sym1* and *sym2*. - fontX: one of the TeX font names, tt, it, rm, cal, sf, bf or - default (non-math) - symX: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' - fontsizeX: the fontsize in points - dpi: the current dots-per-inch + *fontX*: one of the TeX font names:: - sym is a single symbol(alphanum, punct) or a special symbol - like \sigma. + tt, it, rm, cal, sf, bf or default (non-math) + *fontclassX*: TODO + + *symX*: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' + + *fontsizeX*: the fontsize in points + + *dpi*: the current dots-per-inch """ return 0. def get_metrics(self, font, font_class, sym, fontsize, dpi): """ - font: one of the TeX font names, tt, it, rm, cal, sf, bf or - default (non-math) - sym: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' - fontsize: font size in points - dpi: current dots-per-inch + *font*: one of the TeX font names:: - advance - height - width - xmin, xmax, ymin, ymax - the ink rectangle of the glyph - iceberg - the distance from the baseline to the top of the glyph. - horiBearingY in Truetype parlance, height in TeX parlance + tt, it, rm, cal, sf, bf or default (non-math) + + *font_class*: TODO + + *sym*: a symbol in raw TeX form. e.g. '1', 'x' or '\sigma' + + *fontsize*: font size in points + + *dpi*: current dots-per-inch + + Returns an object with the following attributes: + + - *advance*: The advance distance (in points) of the glyph. + + - *height*: The height of the glyph in points. + + - *width*: The width of the glyph in points. + + - *xmin*, *xmax*, *ymin*, *ymax* - the ink rectangle of the glyph + + - *iceberg* - the distance from the baseline to the top of + the glyph. This corresponds to TeX's definition of + "height". """ info = self._get_info(font, font_class, sym, fontsize, dpi) return info.metrics def set_canvas_size(self, w, h, d): - 'Dimension the drawing canvas; may be a noop' + """ + Set the size of the buffer used to render the math expression. + Only really necessary for the bitmap backends. + """ self.width, self.height, self.depth = ceil(w), ceil(h), ceil(d) self.mathtext_backend.set_canvas_size(self.width, self.height, self.depth) def render_glyph(self, ox, oy, facename, font_class, sym, fontsize, dpi): + """ + Draw a glyph at + + - *ox*, *oy*: position + + - *facename*: One of the TeX face names + + - *font_class*: + + - *sym*: TeX symbol name or single character + + - *fontsize*: fontsize in points + + - *dpi*: The dpi to draw at. + """ info = self._get_info(facename, font_class, sym, fontsize, dpi) realpath, stat_key = get_realpath_and_stat(info.font.fname) used_characters = self.used_characters.setdefault( @@ -539,31 +478,52 @@ self.mathtext_backend.render_glyph(ox, oy, info) def render_rect_filled(self, x1, y1, x2, y2): + """ + Draw a filled rectangle from (*x1*, *y1*) to (*x2*, *y2*). + """ self.mathtext_backend.render_rect_filled(x1, y1, x2, y2) def get_xheight(self, font, fontsize, dpi): + """ + Get the xheight for the given *font* and *fontsize*. + """ raise NotImplementedError() def get_underline_thickness(self, font, fontsize, dpi): + """ + Get the line thickness that matches the given font. Used as a + base unit for drawing lines such as in a fraction or radical. + """ raise NotImplementedError() def get_used_characters(self): + """ + Get the set of characters that were used in the math + expression. Used by backends that need to subset fonts so + they know which glyphs to include. + """ return self.used_characters def get_results(self, box): + """ + Get the data needed by the backend to render the math + expression. The return value is backend-specific. + """ return self.mathtext_backend.get_results(box) def get_sized_alternatives_for_symbol(self, fontname, sym): """ Override if your font provides multiple sizes of the same - symbol. + symbol. Should return a list of symbols matching *sym* in + various sizes. The expression renderer will select the most + appropriate size for a given situation from this list. """ return [(fontname, sym)] class TruetypeFonts(Fonts): """ A generic base class for all font setups that use Truetype fonts - (through ft2font) + (through FT2Font). """ class CachedFont: def __init__(self, font): @@ -589,8 +549,6 @@ Fonts.destroy(self) def _get_font(self, font): - """Looks up a CachedFont with its charmap and inverse charmap. - font may be a TeX font name (cal, rm, it etc.), or postscript name.""" if font in self.fontmap: basename = self.fontmap[font] else: @@ -611,7 +569,6 @@ return 0. def _get_info(self, fontname, font_class, sym, fontsize, dpi): - 'load the cmfont, metrics and glyph with caching' key = fontname, font_class, sym, fontsize, dpi bunch = self.glyphd.get(key) if bunch is not None: @@ -665,8 +622,9 @@ return xHeight def get_underline_thickness(self, font, fontsize, dpi): - # This function used to grab underline thickness from the font, - # but that information is just too un-reliable, so it is now hardcoded. + # This function used to grab underline thickness from the font + # metrics, but that information is just too un-reliable, so it + # is now hardcoded. return ((0.75 / 12.0) * fontsize * dpi) / 72.0 def get_kern(self, font1, fontclass1, sym1, fontsize1, @@ -681,7 +639,10 @@ class BakomaFonts(TruetypeFonts): """ - Use the Bakoma true type fonts for rendering + Use the Bakoma TrueType fonts for rendering. + + Symbols are strewn about a number of font files, each of which has + its own proprietary 8-bit encoding. """ _fontmap = { 'cal' : 'cmsy10', 'rm' : 'cmr10', @@ -788,15 +749,19 @@ _size_alternatives[alias] = _size_alternatives[target] def get_sized_alternatives_for_symbol(self, fontname, sym): - alternatives = self._size_alternatives.get(sym) - if alternatives: - return alternatives - return [(fontname, sym)] + return self._size_alternatives.get(sym, [(fontname, sym)]) class UnicodeFonts(TruetypeFonts): - """An abstract base class for handling Unicode fonts. """ + An abstract base class for handling Unicode fonts. + While some reasonably complete Unicode fonts (such as DejaVu) may + work in some situations, the only Unicode font I'm aware of with a + complete set of math symbols is STIX. + + This class will "fallback" on the Bakoma fonts when a required + symbol can not be found in the font. + """ fontmap = {} use_cmex = True @@ -900,7 +865,15 @@ class StixFonts(UnicodeFonts): """ - A font handling class for the STIX fonts + A font handling class for the STIX fonts. + + In addition to what UnicodeFonts provides, this class: + + - supports "virtual fonts" which are complete alpha numeric + character sets with different font styles at special Unicode + code points, such as "Blackboard". + + - handles sized alternative characters for the STIXSizeX fonts. """ _fontmap = { 'rm' : 'STIXGeneral', 'it' : 'STIXGeneral:italic', @@ -994,7 +967,7 @@ class StixSansFonts(StixFonts): """ - A font handling class for the STIX fonts (using sans-serif + A font handling class for the STIX fonts (that uses sans-serif characters by default). """ _sans = True @@ -1187,9 +1160,9 @@ pass class Node(object): - """A node in the TeX box model - node133 """ + A node in the TeX box model + """ def __init__(self): self.size = 0 @@ -1203,21 +1176,26 @@ return 0.0 def shrink(self): - """Shrinks one level smaller. There are only three levels of sizes, - after which things will no longer get smaller.""" + """ + Shrinks one level smaller. There are only three levels of + sizes, after which things will no longer get smaller. + """ self.size += 1 def grow(self): - """Grows one level larger. There is no limit to how big something - can get.""" + """ + Grows one level larger. There is no limit to how big + something can get. + """ self.size -= 1 def render(self, x, y): pass class Box(Node): - """Represents any node with a physical location. - node135""" + """ + Represents any node with a physical location. + """ def __init__(self, width, height, depth): Node.__init__(self) self.width = width @@ -1255,15 +1233,16 @@ Box.__init__(self, width, 0., 0.) class Char(Node): - """Represents a single character. Unlike TeX, the font - information and metrics are stored with each Char to make it - easier to lookup the font metrics when needed. Note that TeX - boxes have a width, height, and depth, unlike Type1 and Truetype - which use a full bounding box and an advance in the x-direction. - The metrics must be converted to the TeX way, and the advance (if - different from width) must be converted into a Kern node when the - Char is added to its parent Hlist. - node134""" + """ + Represents a single character. Unlike TeX, the font information + and metrics are stored with each :class:`Char` to make it easier + to lookup the font metrics when needed. Note that TeX boxes have + a width, height, and depth, unlike Type1 and Truetype which use a + full bounding box and an advance in the x-direction. The metrics + must be converted to the TeX way, and the advance (if different + from width) must be converted into a :class:`Kern` node when the + :class:`Char` is added to its parent :class:`Hlist`. + """ def __init__(self, c, state): Node.__init__(self) self.c = c @@ -1294,9 +1273,11 @@ return self._metrics.slanted def get_kerning(self, next): - """Return the amount of kerning between this and the given + """ + Return the amount of kerning between this and the given character. Called when characters are strung together into - Hlists to create Kern nodes.""" + :class:`Hlist` to create :class:`Kern` nodes. + """ advance = self._metrics.advance - self.width kern = 0. if isinstance(next, Char): @@ -1307,7 +1288,9 @@ return advance + kern def render(self, x, y): - """Render the character to the canvas""" + """ + Render the character to the canvas + """ self.font_output.render_glyph( x, y, self.font, self.font_class, self.c, self.fontsize, self.dpi) @@ -1328,9 +1311,11 @@ self.depth *= GROW_FACTOR class Accent(Char): - """The font metrics need to be dealt with differently for accents, + """ + The font metrics need to be dealt with differently for accents, since they are already offset correctly from the baseline in - TrueType fonts.""" + TrueType fonts. + """ def _update_metrics(self): metrics = self._metrics = self.font_output.get_metrics( self.font, self.font_class, self.c, self.fontsize, self.dpi) @@ -1347,14 +1332,17 @@ self._update_metrics() def render(self, x, y): - """Render the character to the canvas""" + """ + Render the character to the canvas. + """ self.font_output.render_glyph( x - self._metrics.xmin, y + self._metrics.ymin, self.font, self.font_class, self.c, self.fontsize, self.dpi) class List(Box): - """A list of nodes (either horizontal or vertical). - node135""" + """ + A list of nodes (either horizontal or vertical). + """ def __init__(self, elements): Box.__init__(self, 0., 0., 0.) self.shift_amount = 0. # An arbitrary offset @@ -1372,8 +1360,10 @@ ' '.join([repr(x) for x in self.children])) def _determine_order(self, totals): - """A helper function to determine the highest order of glue - used by the members of this list. Used by vpack and hpack.""" + """ + A helper function to determine the highest order of glue + used by the members of this list. Used by vpack and hpack. + """ o = 0 for i in range(len(totals) - 1, 0, -1): if totals[i] != 0.0: @@ -1411,8 +1401,9 @@ self.glue_set *= GROW_FACTOR class Hlist(List): - """A horizontal list of boxes. - node135""" + """ + A horizontal list of boxes. + """ def __init__(self, elements, w=0., m='additional', do_kern=True): List.__init__(self, elements) if do_kern: @@ -1420,10 +1411,13 @@ self.hpack() def kern(self): - """Insert Kern nodes between Chars to set kerning. The - Chars themselves determine the amount of kerning they need - (in get_kerning), and this function just creates the linked - list in the correct way.""" + """ + Insert :class:`Kern` nodes between :class:`Char` nodes to set + kerning. The :class:`Char` nodes themselves determine the + amount of kerning they need (in :meth:`~Char.get_kerning`), + and this function just creates the linked list in the correct + way. + """ new_children = [] num_children = len(self.children) if num_children: @@ -1455,20 +1449,24 @@ # return 0.0 def hpack(self, w=0., m='additional'): - """The main duty of hpack is to compute the dimensions of the - resulting boxes, and to adjust the glue if one of those dimensions is - pre-specified. The computed sizes normally enclose all of the material - inside the new box; but some items may stick out if negative glue is - used, if the box is overfull, or if a \vbox includes other boxes that - have been shifted left. + """ + The main duty of :meth:`hpack` is to compute the dimensions of + the resulting boxes, and to adjust the glue if one of those + dimensions is pre-specified. The computed sizes normally + enclose all of the material inside the new box; but some items + may stick out if negative glue is used, if the box is + overfull, or if a ``\\vbox`` includes other boxes that have + been shifted left. - w: specifies a width - m: is either 'exactly' or 'additional'. + - *w*: specifies a width - Thus, hpack(w, 'exactly') produces a box whose width is exactly w, while - hpack (w, 'additional') yields a box whose width is the natural width - plus w. The default values produce a box with the natural width. - node644, node649""" + - *m*: is either 'exactly' or 'additional'. + + Thus, ``hpack(w, 'exactly')`` produces a box whose width is + exactly *w*, while ``hpack(w, 'additional')`` yields a box + whose width is the natural width plus *w*. The default values + produce a box with the natural width. + """ # I don't know why these get reset in TeX. Shift_amount is pretty # much useless if we do. #self.shift_amount = 0. @@ -1514,25 +1512,28 @@ self._set_glue(x, -1, total_shrink, "Underfull") class Vlist(List): - """A vertical list of boxes. - node137""" + """ + A vertical list of boxes. + """ def __init__(self, elements, h=0., m='additional'): List.__init__(self, elements) self.vpack() def vpack(self, h=0., m='additional', l=float(inf)): - """The main duty of vpack is to compute the dimensions of the - resulting boxes, and to adjust the glue if one of those dimensions is - pre-specified. + """ + The main duty of :meth:`vpack` is to compute the dimensions of + the resulting boxes, and to adjust the glue if one of those + dimensions is pre-specified. - h: specifies a height - m: is either 'exactly' or 'additional'. - l: a maximum height + - *h*: specifies a height + - *m*: is either 'exactly' or 'additional'. + - *l*: a maximum height - Thus, vpack(h, 'exactly') produces a box whose width is exactly w, while - vpack(w, 'additional') yields a box whose width is the natural width - plus w. The default values produce a box with the natural width. - node644, node668""" + Thus, ``vpack(h, 'exactly')`` produces a box whose height is + exactly *h*, while ``vpack(h, 'additional')`` yields a box + whose height is the natural height plus *h*. The default + values produce a box with the natural width. + """ # I don't know why these get reset in TeX. Shift_amount is pretty # much useless if we do. # self.shift_amount = 0. @@ -1585,13 +1586,15 @@ self._set_glue(x, -1, total_shrink, "Underfull") class Rule(Box): - """A Rule node stands for a solid black rectangle; it has width, - depth, and height fields just as in an Hlist. However, if any of these - dimensions is inf, the actual value will be determined by running the - rule up to the boundary of the innermost enclosing box. This is called - a "running dimension." The width is never running in an Hlist; the - height and depth are never running in a Vlist. - node138""" + """ + A :class:`Rule` node stands for a solid black rectangle; it has + *width*, *depth*, and *height* fields just as in an + :class:`Hlist`. However, if any of these dimensions is inf, the + actual value will be determined by running the rule up to the + boundary of the innermost enclosing box. This is called a "running + dimension." The width is never running in an :class:`Hlist`; the + height and depth are never running in a :class:`Vlist`. + """ def __init__(self, width, height, depth, state): Box.__init__(self, width, height, depth) self.font_output = state.font_output @@ -1600,7 +1603,9 @@ self.font_output.render_rect_filled(x, y, x + w, y + h) class Hrule(Rule): - """Convenience class to create a horizontal rule.""" + """ + Convenience class to create a horizontal rule. + """ def __init__(self, state): thickness = state.font_output.get_underline_thickness( state.font, state.fontsize, state.dpi) @@ -1608,18 +1613,21 @@ Rule.__init__(self, inf, height, depth, state) class Vrule(Rule): - """Convenience class to create a vertical rule.""" + """ + Convenience class to create a vertical rule. + """ def __init__(self, state): thickness = state.font_output.get_underline_thickness( state.font, state.fontsize, state.dpi) Rule.__init__(self, thickness, inf, inf, state) class Glue(Node): - """Most of the information in this object is stored in the underlying - GlueSpec class, which is shared between multiple glue objects. (This + """ + Most of the information in this object is stored in the underlying + :class:`GlueSpec` class, which is shared between multiple glue objects. (This is a memory optimization which probably doesn't matter anymore, but it's easier to stick to what TeX does.) - node149, node152""" + """ def __init__(self, glue_type, copy=False): Node.__init__(self) self.glue_subtype = 'normal' @@ -1647,7 +1655,9 @@ self.glue_spec.width *= GROW_FACTOR class GlueSpec(object): - """node150, node151""" + """ + See :class:`Glue`. + """ def __init__(self, width=0., stretch=0., stretch_order=0, shrink=0., shrink_order=0): self.width = width self.stretch = stretch @@ -1709,26 +1719,32 @@ Glue.__init__(self, 'ss') class HCentered(Hlist): - """A convenience class to create an Hlist whose contents are centered - within its enclosing box.""" + """ + A convenience class to create an :class:`Hlist` whose contents are + centered within its enclosing box. + """ def __init__(self, elements): Hlist.__init__(self, [SsGlue()] + elements + [SsGlue()], do_kern=False) class VCentered(Hlist): - """A convenience class to create an Vlist whose contents are centered - within its enclosing box.""" + """ + A convenience class to create a :class:`Vlist` whose contents are + centered within its enclosing box. + """ def __init__(self, elements): Vlist.__init__(self, [SsGlue()] + elements + [SsGlue()]) class Kern(Node): - """A Kern node has a width field to specify a (normally negative) - amount of spacing. This spacing correction appears in horizontal lists - between letters like A and V when the font designer said that it looks - better to move them closer together or further apart. A kern node can - also appear in a vertical list, when its 'width' denotes additional - spacing in the vertical direction. - node155""" + """ + A :class:`Kern` node has a width field to specify a (normally + negative) amount of spacing. This spacing correction appears in + horizontal lists between letters like A and V when the font + designer said that it looks better to move them closer together or + further apart. A kern node can also appear in a vertical list, + when its *width* denotes additional spacing in the vertical + direction. + """ def __init__(self, width): Node.__init__(self) self.width = width @@ -1746,11 +1762,13 @@ self.width *= GROW_FACTOR class SubSuperCluster(Hlist): - """This class is a sort of hack to get around that fact that this - code doesn't parse to an mlist and then an hlist, but goes directly - to hlists. This lets us store enough information in the hlist itself, - namely the nucleas, sub- and super-script, such that if another script - follows that needs to be attached, it can be reconfigured on the fly.""" + """ + :class:`SubSuperCluster` is a sort of hack to get around that fact + that this code do a two-pass parse like TeX. This lets us store + enough information in the hlist itself, namely the nucleus, sub- + and super-script, such that if another script follows that needs + to be attached, it can be reconfigured on the fly. + """ def __init__(self): self.nucleus = None self.sub = None @@ -1758,11 +1776,13 @@ Hlist.__init__(self, []) class AutoHeightChar(Hlist): - """A class that will create a character as close to the given height - and depth as possible. When using a font with multiple height versions - of some characters (such as the BaKoMa fonts), the correct glyph will - be selected, otherwise this will always just return a scaled version - of the glyph.""" + """ + :class:`AutoHeightChar` will create a character as close to the + given height and depth as possible. When using a font with + multiple height versions of some characters (such as the BaKoMa + fonts), the correct glyph will be selected, otherwise this will + always just return a scaled version of the glyph. + """ def __init__(self, c, height, depth, state, always=False): alternatives = state.font_output.get_sized_alternatives_for_symbol( state.font, c) @@ -1784,11 +1804,13 @@ self.shift_amount = shift class AutoWidthChar(Hlist): - """A class that will create a character as close to the given width - as possible. When using a font with multiple width versions - of some characters (such as the BaKoMa fonts), the correct glyph will - be selected, otherwise this will always just return a scaled version - of the glyph.""" + """ + :class:`AutoWidthChar` will create a character as close to the + given width as possible. When using a font with multiple width + versions of some characters (such as the BaKoMa fonts), the + correct glyph will be selected, otherwise this will always just + return a scaled version of the glyph. + """ def __init__(self, c, width, state, always=False, char_class=Char): alternatives = state.font_output.get_sized_alternatives_for_symbol( state.font, c) @@ -1808,13 +1830,15 @@ self.width = char.width class Ship(object): - """Once the boxes have been set up, this sends them to output. - Since boxes can be inside of boxes inside of boxes, the main - work of Ship is done by two mutually recursive routines, hlist_out - and vlist_out , which traverse the Hlists and Vlists inside of - horizontal and vertical boxes. The global variables used in TeX to - store state as it processes have become member variables here. - node592.""" + """ + Once the boxes have been set up, this sends them to output. Since + boxes can be inside of boxes inside of boxes, the main work of + :class:`Ship` is done by two mutually recursive routines, + :meth:`hlist_out` and :meth:`vlist_out`, which traverse the + :class:`Hlist` nodes and :class:`Vlist` nodes inside of horizontal + and vertical boxes. The global variables used in TeX to store + state as it processes have become member variables here. + """ def __call__(self, ox, oy, box): self.max_push = 0 # Deepest nesting of push commands so far self.cur_s = 0 @@ -1959,6 +1983,9 @@ # PARSER def Error(msg): + """ + Helper class to raise parser errors. + """ def raise_error(s, loc, toks): raise ParseFatalException(msg + "\n" + s) @@ -1967,6 +1994,14 @@ return empty class Parser(object): + """ + This is the pyparsing-based parser for math expressions. It + actually parses full strings *containing* math expressions, in + that raw text may also appear outside of pairs of ``$``. + + The grammar is based directly on that in TeX, though it cuts a few + corners. + """ _binary_operators = set(r''' + * \pm \sqcap \rhd @@ -2198,11 +2233,20 @@ self.clear() def clear(self): + """ + Clear any state before parsing. + """ self._expr = None self._state_stack = None self._em_width_cache = {} def parse(self, s, fonts_object, fontsize, dpi): + """ + Parse expression *s* using the given *fonts_object* for + output, at the given *fontsize* and *dpi*. + + Returns the parse tree of :class:`Node` instances. + """ self._state_stack = [self.State(fonts_object, 'default', 'rm', fontsize, dpi)] try: self._expression.parseString(s) @@ -2219,6 +2263,12 @@ # is pushed and popped accordingly. The current state always # exists in the top element of the stack. class State(object): + """ + Stores the state of the parser. + + States are pushed and popped from a stack as necessary, and + the "current" state is always at the top of the stack. + """ def __init__(self, font_output, font, font_class, fontsize, dpi): self.font_output = font_output self._font = font @@ -2243,12 +2293,22 @@ font = property(_get_font, _set_font) def get_state(self): + """ + Get the current :class:`State` of the parser. + """ return self._state_stack[-1] def pop_state(self): + """ + Pop a :class:`State` off of the stack. + """ self._state_stack.pop() def push_state(self): + """ + Push a new :class:`State` onto the stack which is just a copy + of the current state. + """ self._state_stack.append(self.get_state().copy()) def finish(self, s, loc, toks): @@ -2674,14 +2734,6 @@ # MAIN class MathTextParser(object): - """ - Parse the math expression s, return the (bbox, fonts) tuple needed - to render it. - - fontsize must be in points - - return is width, height, fonts - """ _parser = None _backend_mapping = { @@ -2701,10 +2753,23 @@ } def __init__(self, output): + """ + Create a MathTextParser for the given backend *output*. + """ self._output = output.lower() self._cache = maxdict(50) def parse(self, s, dpi = 72, prop = None): + """ + Parse the given math expression *s* at the given *dpi*. If + *prop* is provided, it is a + :class:`~matplotlib.font_manager.FontProperties` object + specifying the "default" font to use in the math expression, + used for all non-math text. + + The results are cached, so multiple calls to :meth:`parse` + with the same expression should be fast. + """ if prop is None: prop = FontProperties() cacheKey = (s, dpi, hash(prop)) @@ -2712,7 +2777,7 @@ if result is not None: return result - if self._output == 'PS' and rcParams['ps.useafm']: + if self._output == 'ps' and rcParams['ps.useafm']: font_output = StandardPsFonts(prop) else: backend = self._backend_mapping[self._output]() @@ -2748,6 +2813,15 @@ def to_mask(self, texstr, dpi=120, fontsize=14): """ + *texstr* + A valid mathtext string, eg r'IQ: $\sigma_i=15$' + + *dpi* + The dots-per-inch to render the text + + *fontsize* + The font size in points + Returns a tuple (*array*, *depth*) - *array* is an NxM uint8 alpha ubyte mask array of @@ -2755,15 +2829,6 @@ - depth is the offset of the baseline from the bottom of the image in pixels. - - ''texstr'' - A valid mathtext string, eg r'IQ: $\sigma_i=15$' - - ''dpi'' - The dots-per-inch to render the text - - ''fontsize'' - The font size in points """ assert(self._output=="bitmap") prop = FontProperties(size=fontsize) @@ -2774,27 +2839,25 @@ def to_rgba(self, texstr, color='black', dpi=120, fontsize=14): """ - Returns a tuple (*array*, *depth*) + *texstr* + A valid mathtext string, eg r'IQ: $\sigma_i=15$' - - *array* is an NxMx4 RGBA array of ubyte rasterized tex. + *color* + Any matplotlib color argument - - depth is the offset of the baseline from the bottom of the - image in pixels. + *dpi* + The dots-per-inch to render the text - Returns a tuple (array, depth), where depth is the offset of - the baseline from the bottom of the image. + *fontsize* + The font size in points - ''texstr'' - A valid mathtext string, eg r'IQ: $\sigma_i=15$' + Returns a tuple (*array*, *depth*) - ''color'' - A valid matplotlib color argument + - *array* is an NxM uint8 alpha ubyte mask array of + rasterized tex. - ''dpi'' - The dots-per-inch to render the text - - ''fontsize'' - The font size in points + - depth is the offset of the baseline from the bottom of the + image in pixels. """ x, depth = self.to_mask(texstr, dpi=dpi, fontsize=fontsize) @@ -2813,22 +2876,24 @@ Returns the offset of the baseline from the bottom of the image in pixels. - ''filename'' + *filename* A writable filename or fileobject - ''texstr'' + *texstr* A valid mathtext string, eg r'IQ: $\sigma_i=15$' - ''color'' + *color* A valid matplotlib color argument - ''dpi'' + *dpi* The dots-per-inch to render the text - ''fontsize'' + *fontsize* The font size in points - """ + Returns the offset of the baseline from the bottom of the + image in pixels. + """ rgba, depth = self.to_rgba(texstr, color=color, dpi=dpi, fontsize=fontsize) numrows, numcols, tmp = rgba.shape @@ -2840,16 +2905,15 @@ Returns the offset of the baseline from the bottom of the image in pixels. - ''texstr'' + *texstr* A valid mathtext string, eg r'IQ: $\sigma_i=15$' - ''dpi'' + *dpi* The dots-per-inch to render the text - ''fontsize'' + *fontsize* The font size in points - - """ + """ assert(self._output=="bitmap") prop = FontProperties(size=fontsize) ftimage, depth = self.parse(texstr, dpi=dpi, prop=prop) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-10-27 18:52:45
|
Revision: 6338 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6338&view=rev Author: mdboom Date: 2008-10-27 18:52:41 +0000 (Mon, 27 Oct 2008) Log Message: ----------- More docs. Modified Paths: -------------- trunk/matplotlib/doc/api/index.rst trunk/matplotlib/doc/devel/outline.rst trunk/matplotlib/lib/matplotlib/font_manager.py trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py trunk/matplotlib/lib/matplotlib/lines.py trunk/matplotlib/lib/matplotlib/mlab.py Added Paths: ----------- trunk/matplotlib/doc/api/font_manager_api.rst Added: trunk/matplotlib/doc/api/font_manager_api.rst =================================================================== --- trunk/matplotlib/doc/api/font_manager_api.rst (rev 0) +++ trunk/matplotlib/doc/api/font_manager_api.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -0,0 +1,21 @@ +*********************** +matplotlib font_manager +*********************** + +:mod:`matplotlib.font_manager` +============================== + +.. automodule:: matplotlib.font_manager + :members: + :undoc-members: + :show-inheritance: + +:mod:`matplotlib.fontconfig_pattern` +============================== + +.. automodule:: matplotlib.fontconfig_pattern + :members: + :undoc-members: + :show-inheritance: + + Modified: trunk/matplotlib/doc/api/index.rst =================================================================== --- trunk/matplotlib/doc/api/index.rst 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/doc/api/index.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -14,7 +14,6 @@ matplotlib_configuration_api.rst afm_api.rst artist_api.rst - figure_api.rst axes_api.rst axis_api.rst cbook_api.rst @@ -22,6 +21,8 @@ collections_api.rst colorbar_api.rst colors_api.rst + figure_api.rst + font_manager_api.rst nxutils_api.rst mathtext_api.rst mlab_api.rst Modified: trunk/matplotlib/doc/devel/outline.rst =================================================================== --- trunk/matplotlib/doc/devel/outline.rst 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/doc/devel/outline.rst 2008-10-27 18:52:41 UTC (rev 6338) @@ -126,17 +126,17 @@ dviread Darren needs conversion figure Darren needs conversion finance Darren needs conversion -font_manager Mike needs conversion -fontconfig_pattern Mike needs conversion +font_manager Mike converted +fontconfig_pattern Mike converted image needs conversion legend needs conversion -lines needs conversion +lines Mike & ??? converted mathtext Mike converted -mlab needs conversion -mpl needs conversion +mlab John/Mike converted +mpl N/A patches Mike converted path Mike converted -pylab no docstrings +pylab N/A pyplot converted quiver needs conversion rcsetup needs conversion Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,17 +1,26 @@ """ -A module for finding, managing, and using fonts across-platforms. +A module for finding, managing, and using fonts across platforms. -This module provides a single FontManager that can be shared across -backends and platforms. The findfont() function returns the best -TrueType (TTF) font file in the local or system font path that matches -the specified FontProperties. The FontManager also handles Adobe Font -Metrics (AFM) font files for use by the PostScript backend. +This module provides a single :class:`FontManager` instance that can +be shared across backends and platforms. The :func:`findfont` +function returns the best TrueType (TTF) font file in the local or +system font path that matches the specified FontProperties. The +FontManager also handles Adobe Font Metrics (AFM) font files for use +by the PostScript backend. -The design is based on the W3C Cascading Style Sheet, Level 1 (CSS1) -font specification (http://www.w3.org/TR/1998/REC-CSS2-19980512/ ). +The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) +font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_. Future versions may implement the Level 2 or 2.1 specifications. +Experimental support is included for using `fontconfig +<http://www.fontconfig.org>`_ on Unix variant plaforms (Linux, OS X, +Solaris). To enable it, set the constant ``USE_FONTCONFIG`` in this +file to ``True``. Fontconfig has the advantage that it is the +standard way to look up fonts on X11 platforms, so if a font is +installed, it is much more likely to be found. +""" +""" KNOWN ISSUES - documentation @@ -24,9 +33,9 @@ - 'light' is an invalid weight value, remove it. - update_fonts not implemented - Authors : John Hunter <jdh...@ac...> Paul Barrett <Ba...@ST...> + Michael Droettboom <md...@ST...> Copyright : John Hunter (2004,2005), Paul Barrett (2004,2005) License : matplotlib license (PSF compatible) The font directory code is from ttfquery, @@ -99,12 +108,21 @@ X11FontDirectories.append(path) def get_fontext_synonyms(fontext): + """ + Return a list of file extensions extensions that are synonyms for + the given file extension *fileext*. + """ return {'ttf': ('ttf', 'otf'), + 'otf': ('ttf', 'otf'), 'afm': ('afm',)}[fontext] def win32FontDirectory(): - """Return the user-specified font directory for Win32.""" + """Return the user-specified font directory for Win32. This is + looked up from the registry key:: + \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts + + If the key is not found, $WINDIR/Fonts will be returned.""" try: import _winreg except ImportError: @@ -126,8 +144,9 @@ def win32InstalledFonts(directory=None, fontext='ttf'): """ Search for fonts in the specified font directory, or use the - system directories if none given. A list of TrueType fonts are - returned by default with AFM fonts as an option. + system directories if none given. A list of TrueType font + filenames are returned by default, or AFM fonts if *fontext* == + 'afm'. """ import _winreg @@ -168,11 +187,16 @@ return None def OSXFontDirectory(): - """Return the system font directories for OS X.""" - + """ + Return the system font directories for OS X. This is done by + starting at the list of hardcoded paths in + :attr:`OSXFontDirectories` and returning all nested directories + within them. + """ fontpaths = [] def add(arg,directory,files): fontpaths.append(directory) + for fontdir in OSXFontDirectories: try: if os.path.isdir(fontdir): @@ -182,7 +206,9 @@ return fontpaths def OSXInstalledFonts(directory=None, fontext='ttf'): - """Get list of font files on OS X - ignores font suffix by default""" + """ + Get list of font files on OS X - ignores font suffix by default. + """ if directory is None: directory = OSXFontDirectory() @@ -200,7 +226,12 @@ def x11FontDirectory(): - """Return the system font directories for X11.""" + """ + Return the system font directories for X11. This is done by + starting at the list of hardcoded paths in + :attr:`X11FontDirectories` and returning all nested directories + within them. + """ fontpaths = [] def add(arg,directory,files): fontpaths.append(directory) @@ -213,10 +244,14 @@ pass return fontpaths + def get_fontconfig_fonts(fontext='ttf'): - """Grab a list of all the fonts that are being tracked by fontconfig. - This is an easy way to grab all of the fonts the user wants to be made - available to applications, without knowing where all of them reside.""" + """ + Grab a list of all the fonts that are being tracked by fontconfig + by making a system call to ``fc-list``. This is an easy way to + grab all of the fonts the user wants to be made available to + applications, without needing knowing where all of them reside. + """ try: import commands except ImportError: @@ -299,9 +334,8 @@ class FontKey(object): """ A class for storing Font properties. It is used when populating - the font dictionary. + the font lookup dictionary. """ - def __init__(self, name ='', style ='normal', @@ -323,8 +357,10 @@ def ttfFontProperty(font): """ - A function for populating the FontKey by extracting information - from the TrueType font file. + A function for populating the :class:`FontKey` by extracting + information from the TrueType font file. + + *font* is an :class:`FT2Font` instance. """ name = font.family_name @@ -416,8 +452,10 @@ def afmFontProperty(font): """ - A function for populating the FontKey by extracting information - from the AFM font file. + A function for populating a :class:`FontKey` instance by + extracting information from the AFM font file. + + *font* is a class:`AFM` instance. """ name = font.get_familyname() @@ -475,8 +513,8 @@ def add_filename(fontdict, prop, fname): """ A function to add a font file name to the font dictionary using - the FontKey properties. If a font property has no dictionary, then - create it. + the :class:`FontKey` properties. If a font property has no + dictionary, then create it. """ try: size = str(float(prop.size)) @@ -494,9 +532,9 @@ def createFontDict(fontfiles, fontext='ttf'): """ - A function to create a dictionary of font file paths. The - default is to create a dictionary for TrueType fonts. An AFM font - dictionary can optionally be created. + A function to create a font lookup dictionary. The default is to + create a dictionary for TrueType fonts. An AFM font dictionary + can optionally be created. """ fontdict = {} @@ -589,42 +627,55 @@ """ A class for storing and manipulating font properties. - The font properties are those described in the W3C Cascading Style - Sheet, Level 1 (CSS1; http://www.w3.org/TR/1998/REC-CSS2-19980512/) - font specification. The six properties are: + The font properties are those described in the `W3C Cascading + Style Sheet, Level 1 + <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ font + specification. The six properties are: - family - A list of font names in decreasing order of priority. - The last item is the default font name and is given the - name of the font family, either serif, sans-serif, - cursive, fantasy, and monospace. - style - Either normal, italic or oblique. - variant - Either normal or small-caps. - stretch - Either an absolute value of ultra-condensed, extra- - condensed, condensed, semi-condensed, normal, semi- - expanded, expanded, extra-expanded or ultra-expanded; - or a relative value of narrower or wider. - This property is currently not implemented and is set to - normal. - weight - A numeric value in the range 100, 200, 300, ..., 900. - size - Either an absolute value of xx-small, x-small, small, - medium, large, x-large, xx-large; or a relative value - of smaller or larger; or an absolute font size, e.g. 12; - or scalable. + - family: A list of font names in decreasing order of priority. + The last item is the default font name and is given the name + of the font family, either 'serif', 'sans-serif', 'cursive', + 'fantasy', or 'monospace'. - The default font property for TrueType fonts is: sans-serif, normal, - normal, normal, 400, scalable. + - style: Either 'normal', 'italic' or 'oblique'. - The preferred usage of font sizes is to use the relative values, e.g. - large, instead of absolute font sizes, e.g. 12. This approach allows - all text sizes to be made larger or smaller based on the font manager's - default font size, i.e. by using the set_default_size() method of the - font manager. + - variant: Either 'normal' or 'small-caps'. - This class will also accept a fontconfig pattern, if it is the only - argument provided. fontconfig patterns are described here: + - stretch: Either an absolute value of 'ultra-condensed', + 'extra- condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded' or + 'ultra-expanded'; or a relative value of narrower or wider. + This property is currently not implemented and is set to + normal. - http://www.fontconfig.org/fontconfig-user.html + - weight: A numeric value in the range 100, 200, 300, ..., 900. + - size: Either an absolute value of 'xx-small', 'x-small', 'small', + 'medium', 'large', 'x-large', 'xx-large'; or a relative value + of smaller or larger; or an absolute font size, e.g. 12; + or 'scalable'. + + The default font property for TrueType fonts is:: + + sans-serif, normal, normal, normal, 400, scalable. + + Alternatively, a font may be specified using an absolute path to a + .ttf file, by using the *fname* kwarg. + + The preferred usage of font sizes is to use the relative values, + e.g. 'large', instead of absolute font sizes, e.g. 12. This + approach allows all text sizes to be made larger or smaller based + on the font manager's default font size, i.e. by using the + :meth:`FontManager.set_default_size` method. + + This class will also accept a `fontconfig + <http://www.fontconfig.org/>`_ pattern, if it is the only argument + provided. See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. This support + does not require fontconfig to be installed or support for it to + be enabled. We are merely borrowing its pattern syntax for use + here. + Note that matplotlib's internal font manager and fontconfig use a different algorithm to lookup fonts, so the results of the same pattern may be different in matplotlib than in other applications that use @@ -684,8 +735,9 @@ return self.get_fontconfig_pattern() def get_family(self): - """Return a list of font names that comprise the font family. """ + Return a list of font names that comprise the font family. + """ if self._family is None: family = rcParams['font.family'] if is_string_like(family): @@ -694,25 +746,33 @@ return self._family def get_name(self): - """Return the name of the font that best matches the font properties.""" + """ + Return the name of the font that best matches the font + properties. + """ return ft2font.FT2Font(str(findfont(self))).family_name def get_style(self): - """Return the font style. Values are: normal, italic or oblique.""" + """ + Return the font style. Values are: 'normal', 'italic' or + 'oblique'. + """ if self._slant is None: return rcParams['font.style'] return self._slant def get_variant(self): - """Return the font variant. Values are: normal or small-caps.""" + """ + Return the font variant. Values are: 'normal' or + 'small-caps'. + """ if self._variant is None: return rcParams['font.variant'] return self._variant def get_weight(self): """ - Return the font weight. See the FontProperties class for a - a list of possible values. + Return the font weight. """ if self._weight is None: return rcParams['font.weight'] @@ -720,30 +780,46 @@ def get_stretch(self): """ - Return the font stretch or width. Options are: normal, - narrow, condensed, or wide. + Return the font stretch or width. Options are: 'normal', + 'narrow', 'condensed', or 'wide'. """ if self._stretch is None: return rcParams['font.stretch'] return self._stretch def get_size(self): - """Return the font size.""" + """ + Return the font size. + """ if self._size is None: return rcParams['font.size'] return float(self._size) def get_file(self): + """ + Return the filename of the associated font. + """ return self._file def get_fontconfig_pattern(self): + """ + Get a fontconfig pattern suitable for looking up the font as + specified with fontconfig's ``fc-match`` utility. + + See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. + + This support does not require fontconfig to be installed or + support for it to be enabled. We are merely borrowing its + pattern syntax for use here. + """ return generate_fontconfig_pattern(self) def set_family(self, family): """ Change the font family. May be either an alias (generic name - is CSS parlance), such as: serif, sans-serif, cursive, - fantasy, or monospace, or a real font name. + is CSS parlance), such as: 'serif', 'sans-serif', 'cursive', + 'fantasy', or 'monospace', or a real font name. """ if family is None: self._family = None @@ -754,22 +830,26 @@ set_name = set_family def set_style(self, style): - """Set the font style. Values are: normal, italic or oblique.""" + """ + Set the font style. Values are: 'normal', 'italic' or + 'oblique'. + """ if style not in ('normal', 'italic', 'oblique', None): raise ValueError("style must be normal, italic or oblique") self._slant = style set_slant = set_style def set_variant(self, variant): - """Set the font variant. Values are: normal or small-caps.""" + """ + Set the font variant. Values are: 'normal' or 'small-caps'. + """ if variant not in ('normal', 'small-caps', None): raise ValueError("variant must be normal or small-caps") self._variant = variant def set_weight(self, weight): """ - Set the font weight. See the FontProperties class for a - a list of possible values. + Set the font weight. """ if (weight is not None and weight not in weight_dict and @@ -779,15 +859,20 @@ def set_stretch(self, stretch): """ - Set the font stretch or width. Options are: normal, narrow, - condensed, or wide. + Set the font stretch or width. Options are: 'normal', 'narrow', + 'condensed', or 'wide'. """ if stretch not in ('normal', 'narrow', 'condensed', 'wide', None): raise ValueError("stretch is invalid") self._stretch = stretch def set_size(self, size): - """Set the font size.""" + """ + Set the font size. Either an absolute value of 'xx-small', + 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'; + or a relative value of smaller or larger; or an absolute font + size, e.g. 12; or 'scalable'. + """ if size is None: self._size = None else: @@ -803,13 +888,26 @@ size = parent_size assert(type(size) in (int, float)) self._size = size + get_size_in_points = get_size def set_file(self, file): + """ + Set the filename of the fontfile to use. In this case, all + other properties will be ignored. + """ self._file = file - get_size_in_points = get_size + def set_fontconfig_pattern(self, pattern): + """ + Set the properties by parsing a fontconfig *pattern*. - def set_fontconfig_pattern(self, pattern): + See the documentation on `fontconfig patterns + <http://www.fontconfig.org/fontconfig-user.html>`_. + + This support does not require fontconfig to be installed or + support for it to be enabled. We are merely borrowing its + pattern syntax for use here. + """ for key, val in self._parse_fontconfig_pattern(pattern).items(): if type(val) == list: getattr(self, "set_" + key)(val[0]) @@ -821,7 +919,9 @@ return FontProperties(_init = self) def ttfdict_to_fnames(d): - 'flatten a ttfdict to all the filenames it contains' + """ + flatten a ttfdict to all the filenames it contains + """ fnames = [] for named in d.values(): for styled in named.values(): @@ -833,8 +933,10 @@ return fnames def pickle_dump(data, filename): - """Equivalent to pickle.dump(data, open(filename, 'w')) - but closes the file to prevent filehandle leakage.""" + """ + Equivalent to pickle.dump(data, open(filename, 'w')) + but closes the file to prevent filehandle leakage. + """ fh = open(filename, 'w') try: pickle.dump(data, fh) @@ -842,8 +944,10 @@ fh.close() def pickle_load(filename): - """Equivalent to pickle.load(open(filename, 'r')) - but closes the file to prevent filehandle leakage.""" + """ + Equivalent to pickle.load(open(filename, 'r')) + but closes the file to prevent filehandle leakage. + """ fh = open(filename, 'r') try: data = pickle.load(fh) @@ -853,15 +957,19 @@ class FontManager: """ - On import, the FontManager creates a dictionary of TrueType - fonts based on the font properties: name, style, variant, weight, - stretch, and size. The findfont() method searches this dictionary - for a font file name that exactly matches the font properties of the - specified text. If none is found, a default font is returned. By - updating the dictionary with the properties of the found font, the - font dictionary can act like a font cache. + On import, the :class:`FontManager` singleton instance creates a + dictionary of TrueType fonts based on the font properties: name, + style, variant, weight, stretch, and size. The :meth:`findfont` + method searches this dictionary for a font file name that exactly + matches the font properties of the specified text. If none is + found, a default font is returned. By updating the dictionary + with the properties of the found font, the font dictionary can act + like a font cache. + + .. note:: The font lookup mechanism is very exact and brittle. If + the *exact* font specified is not found, a default font is + always returned. This should be improved in the future. """ - def __init__(self, size=None, weight='normal'): self.__default_weight = weight self.default_size = size @@ -870,7 +978,6 @@ os.path.join(rcParams['datapath'],'fonts','afm')] # Create list of font paths - for pathname in ['TTFPATH', 'AFMPATH']: if pathname in os.environ: ttfpath = os.environ[pathname] @@ -913,21 +1020,30 @@ self.afmdict = createFontDict(self.afmfiles, fontext='afm') def get_default_weight(self): - "Return the default font weight." + """ + Return the default font weight. + """ return self.__default_weight def get_default_size(self): - "Return the default font size." + """ + Return the default font size. + """ if self.default_size is None: return rcParams['font.size'] return self.default_size def set_default_weight(self, weight): - "Set the default font weight. The initial value is 'normal'." + """ + Set the default font weight. The initial value is 'normal'. + """ self.__default_weight = weight def set_default_size(self, size): - "Set the default font size in points. The initial value is set by font.size in rc." + """ + Set the default font size in points. The initial value is set + by ``font.size`` in rc. + """ self.default_size = size def update_fonts(self, filenames): @@ -941,16 +1057,17 @@ def findfont(self, prop, fontext='ttf'): """ Search the font dictionary for a font that exactly or closely - matches the specified font properties. See the FontProperties class - for a description. + matches the specified font properties. See the + :class:`FontProperties` class for a description. - The properties are searched in the following order: name, style, - variant, weight, stretch, and size. The font weight always matches - returning the closest weight, and the font size always matches for - scalable fonts. An oblique style font will be used inplace of a - missing italic style font if present. See the W3C Cascading Style - Sheet, Level 1 (CSS1; http://www.w3.org/TR/1998/REC-CSS2-19980512/) - documentation for a description of the font finding algorithm. + The properties are searched in the following order: name, + style, variant, weight, stretch, and size. The font weight + always matches returning the closest weight, and the font size + always matches for scalable fonts. An oblique style font will + be used inplace of a missing italic style font if present. + See the `W3C Cascading Style Sheet, Level 1 + <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ documentation + for a description of the font finding algorithm. """ debug = False if is_string_like(prop): @@ -1067,7 +1184,8 @@ def is_opentype_cff_font(filename): """ Returns True if the given font is a Postscript Compact Font Format - Font embedded in an OpenType wrapper. + Font embedded in an OpenType wrapper. Used by the PostScript and + PDF backends that can not subset these fonts. """ if os.path.splitext(filename)[1].lower() == '.otf': result = _is_opentype_cff_font_cache.get(filename) @@ -1080,7 +1198,7 @@ return result return False - +# The experimental fontconfig-based backend. if USE_FONTCONFIG and sys.platform != 'win32': import re Modified: trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py =================================================================== --- trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,22 +1,24 @@ """ -A module for parsing a fontconfig pattern. +A module for parsing and generating fontconfig patterns. -This class is defined here because it must be available in: - - The old-style config framework (rcsetup.py) - - The traits-based config framework (mpltraits.py) - - The font manager (font_manager.py) +See the `fontconfig pattern specification +<http://www.fontconfig.org/fontconfig-user.html>`_ for more +information. +""" -It probably logically belongs in font_manager.py, but -placing it in any of these places would have created cyclical -dependency problems, or an undesired dependency on traits even -when the traits-based config framework is not used. +# Author : Michael Droettboom <md...@st...> +# License : matplotlib license (PSF compatible) -See here for a rough specification of these patterns: -http://www.fontconfig.org/fontconfig-user.html +# This class is defined here because it must be available in: +# - The old-style config framework (:file:`rcsetup.py`) +# - The traits-based config framework (:file:`mpltraits.py`) +# - The font manager (:file:`font_manager.py`) -Author : Michael Droettboom <md...@st...> -License : matplotlib license (PSF compatible) -""" +# It probably logically belongs in :file:`font_manager.py`, but +# placing it in any of these places would have created cyclical +# dependency problems, or an undesired dependency on traits even +# when the traits-based config framework is not used. + import re from matplotlib.pyparsing import Literal, ZeroOrMore, \ Optional, Regex, StringEnd, ParseException, Suppress @@ -32,11 +34,11 @@ class FontconfigPatternParser: """A simple pyparsing-based parser for fontconfig-style patterns. - See here for a rough specification of these patterns: - http://www.fontconfig.org/fontconfig-user.html + See the `fontconfig pattern specification + <http://www.fontconfig.org/fontconfig-user.html>`_ for more + information. """ - _constants = { 'thin' : ('weight', 'light'), 'extralight' : ('weight', 'light'), @@ -113,6 +115,11 @@ self.ParseException = ParseException def parse(self, pattern): + """ + Parse the given fontconfig *pattern* and return a dictionary + of key/value pairs useful for initializing a + :class:`font_manager.FontProperties` object. + """ props = self._properties = {} try: self._parser.parseString(pattern) @@ -156,8 +163,10 @@ parse_fontconfig_pattern = FontconfigPatternParser().parse def generate_fontconfig_pattern(d): - """Given a dictionary of key/value pairs, generates a fontconfig pattern - string.""" + """ + Given a dictionary of key/value pairs, generates a fontconfig + pattern string. + """ props = [] families = '' size = '' Modified: trunk/matplotlib/lib/matplotlib/lines.py =================================================================== --- trunk/matplotlib/lib/matplotlib/lines.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/lines.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -1,6 +1,6 @@ """ This module contains all the 2D line class which can draw with a -variety of line styles, markers and colors +variety of line styles, markers and colors. """ # TODO: expose cap and join style attrs @@ -32,10 +32,12 @@ return _unmasked_index_ranges(mask, compressed=compressed) -def segment_hits(cx,cy,x,y,radius): - """Determine if any line segments are within radius of a point. Returns - the list of line segments that are within that radius. +def segment_hits(cx, cy, x, y, radius): """ + Determine if any line segments are within radius of a + point. Returns the list of line segments that are within that + radius. + """ # Process single points specially if len(x) < 2: res, = np.nonzero( (cx - x)**2 + (cy - y)**2 <= radius**2 ) @@ -173,7 +175,8 @@ Create a :class:`~matplotlib.lines.Line2D` instance with *x* and *y* data in sequences *xdata*, *ydata*. - The kwargs are Line2D properties: + The kwargs are :class:`~matplotlib.lines.Line2D` properties: + %(Line2D)s """ Artist.__init__(self) @@ -239,8 +242,9 @@ Test whether the mouse event occurred on the line. The pick radius determines the precision of the location test (usually within five points of the value). Use - :meth:`~matplotlib.lines.Line2D.get_pickradius`/:meth:`~matplotlib.lines.Line2D.set_pickradius` - to view or modify it. + :meth:`~matplotlib.lines.Line2D.get_pickradius` or + :meth:`~matplotlib.lines.Line2D.set_pickradius` to view or + modify it. Returns *True* if any values are within the radius along with ``{'ind': pointlist}``, where *pointlist* is the set of points Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-10-27 18:04:58 UTC (rev 6337) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-10-27 18:52:41 UTC (rev 6338) @@ -265,9 +265,9 @@ vectors see numpy.blackman, numpy.hamming, numpy.bartlett, scipy.signal, scipy.signal.get_window etc. - If length x < NFFT, it will be zero padded to NFFT + If len(*x*) < *NFFT*, it will be zero padded to *NFFT*. - Returns the tuple (*Pxx*, *freqs*) + Returns the tuple (*Pxx*, *freqs*). Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) @@ -318,26 +318,26 @@ window=window_hanning, noverlap=0): """ The cross spectral density Pxy by Welches average periodogram - method. The vectors x and y are divided into NFFT length - segments. Each segment is detrended by function detrend and - windowed by function window. noverlap gives the length of the - overlap between segments. The product of the direct FFTs of x and - y are averaged over each segment to compute Pxy, with a scaling to - correct for power loss due to windowing. Fs is the sampling - frequency. + method. The vectors *x* and *y* are divided into *NFFT* length + segments. Each segment is detrended by function *detrend* and + windowed by function *window*. *noverlap* gives the length of the + overlap between segments. The product of the direct FFTs of *x* + and *y* are averaged over each segment to compute *Pxy*, with a + scaling to correct for power loss due to windowing. *Fs* is the + sampling frequency. - NFFT must be even; a power of 2 is most efficient + *NFFT* must be even; a power of 2 is most efficient - window can be a function or a vector of length NFFT. To create - window vectors see numpy.blackman, numpy.hamming, numpy.bartlett, - scipy.signal, scipy.signal.get_window etc. + *window* can be a function or a vector of length *NFFT*. To create + window vectors see :func:`numpy.blackman`, :func:`numpy.hamming`, + :func:`numpy.bartlett`, :func:`scipy.signal`, + :func:`scipy.signal.get_window` etc. - Returns the tuple Pxy, freqs + Returns the tuple (*Pxy*, *freqs*) Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) - """ if NFFT % 2: @@ -393,14 +393,15 @@ def specgram(x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=128): """ - Compute a spectrogram of data in x. Data are split into NFFT + Compute a spectrogram of data in *x*. Data are split into *NFFT* length segements and the PSD of each section is computed. The - windowing function window is applied to each segment, and the - amount of overlap of each segment is specified with noverlap. + windowing function *window* is applied to each segment, and the + amount of overlap of each segment is specified with *noverlap*. - window can be a function or a vector of length NFFT. To create - window vectors see numpy.blackman, numpy.hamming, numpy.bartlett, - scipy.signal, scipy.signal.get_window etc. + *window* can be a function or a vector of length *NFFT*. To create + window vectors see :func:`numpy.blackman`, :func:`numpy.hamming`, + :func:`numpy.bartlett`, :func:`scipy.signal`, + :func:`scipy.signal.get_window` etc. If *x* is real (i.e. non-complex) only the positive spectrum is given. If *x* is complex then the complete spectrum is given. @@ -474,13 +475,13 @@ -_coh_error = """Coherence is calculated by averaging over NFFT -length segments. Your signal is too short for your choice of NFFT. +_coh_error = """Coherence is calculated by averaging over *NFFT* +length segments. Your signal is too short for your choice of *NFFT*. """ def cohere(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=0): """ - The coherence between x and y. Coherence is the normalized + The coherence between *x* and *y*. Coherence is the normalized cross spectral density: .. math:: @@ -510,17 +511,20 @@ def corrcoef(*args): """ - corrcoef(X) where X is a matrix returns a matrix of correlation - coefficients for the columns of X. + corrcoef(*X*) where *X* is a matrix returns a matrix of correlation + coefficients for the columns of *X* - corrcoef(x,y) where x and y are vectors returns the matrix of - correlation coefficients for x and y. + corrcoef(*x*, *y*) where *x* and *y* are vectors returns the matrix of + correlation coefficients for *x* and *y*. - Numpy arrays can be real or complex + Numpy arrays can be real or complex. - The correlation matrix is defined from the covariance matrix C as + The correlation matrix is defined from the covariance matrix *C* + as - r(i,j) = C[i,j] / sqrt(C[i,i]*C[j,j]) + .. math:: + + r_{ij} = \\frac{C_{ij}}{\\sqrt{C_{ii}C_{jj}}} """ warnings.warn("Use numpy.corrcoef", DeprecationWarning) kw = dict(rowvar=False) @@ -528,11 +532,12 @@ def polyfit(*args, **kwargs): - """ - def polyfit(x,y,N) + u""" + polyfit(*x*, *y*, *N*) - Do a best fit polynomial of order N of y to x. Return value is a - vector of polynomial coefficients [pk ... p1 p0]. Eg, for N=2 + Do a best fit polynomial of order *N* of *y* to *x*. Return value + is a vector of polynomial coefficients [pk ... p1 p0]. Eg, for + *N*=2:: p2*x0^2 + p1*x0 + p0 = y1 p2*x1^2 + p1*x1 + p0 = y1 @@ -541,28 +546,32 @@ p2*xk^2 + p1*xk + p0 = yk - Method: if X is a the Vandermonde Matrix computed from x (see - `vandermonds <http://mathworld.wolfram.com/VandermondeMatrix.html>`_), then the - polynomial least squares solution is given by the 'p' in + Method: if *X* is a the Vandermonde Matrix computed from *x* (see + `vandermonds + <http://mathworld.wolfram.com/VandermondeMatrix.html>`_), then the + polynomial least squares solution is given by the '*p*' in X*p = y - where X is a len(x) x N+1 matrix, p is a N+1 length vector, and y - is a len(x) x 1 vector + where *X* is a (len(*x*) \N{MULTIPLICATION SIGN} *N* + 1) matrix, + *p* is a *N*+1 length vector, and *y* is a (len(*x*) + \N{MULTIPLICATION SIGN} 1) vector. This equation can be solved as - p = (XT*X)^-1 * XT * y + .. math:: - where XT is the transpose of X and -1 denotes the inverse. - Numerically, however, this is not a good method, so we use - numpy.linalg.lstsq. + p = (X_t X)^-1 X_t y - For more info, see - `least squares fitting <http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html>`_, - but note that the k's and n's in the superscripts and subscripts - on that page. The linear algebra is correct, however. + where :math:`X_t` is the transpose of *X* and -1 denotes the + inverse. Numerically, however, this is not a good method, so we + use :func:`numpy.linalg.lstsq`. + For more info, see `least squares fitting + <http://mathworld.wolfram.com/LeastSquaresFittingPolynomial.html>`_, + but note that the *k*'s and *n*'s in the superscripts and + subscripts on that page. The linear algebra is correct, however. + .. seealso:: :func:`polyval` """ @@ -574,12 +583,12 @@ def polyval(*args, **kwargs): """ - y = polyval(p,x) + *y* = polyval(*p*, *x*) - p is a vector of polynomial coeffients and y is the polynomial - evaluated at x. + *p* is a vector of polynomial coeffients and *y* is the polynomial + evaluated at *x*. - Example code to remove a polynomial (quadratic) trend from y: + Example code to remove a polynomial (quadratic) trend from y:: p = polyfit(x, y, 2) trend = polyval(p, x) @@ -593,12 +602,11 @@ def vander(*args, **kwargs): """ - X = vander(x,N=None) + *X* = vander(*x*, *N* = *None*) - The Vandermonde matrix of vector x. The i-th column of X is the - the i-th power of x. N is the maximum power to compute; if N is - None it defaults to len(x). - + The Vandermonde matrix of vector *x*. The *i*-th column of *X* is the + the *i*-th power of *x*. *N* is the maximum power to compute; if *N* is + *None* it defaults to len(*x*). """ warnings.warn("Use numpy.vander()", DeprecationWarning) return np.vander(*args, **kwargs) @@ -613,61 +621,63 @@ progressCallback=donothing_callback, returnPxx=False): - """ - Cxy, Phase, freqs = cohere_pairs( X, ij, ...) + u""" + Cxy, Phase, freqs = cohere_pairs(X, ij, ...) - Compute the coherence for all pairs in ij. X is a - numSamples,numCols numpy array. ij is a list of tuples (i,j). - Each tuple is a pair of indexes into the columns of X for which - you want to compute coherence. For example, if X has 64 columns, - and you want to compute all nonredundant pairs, define ij as + Compute the coherence for all pairs in *ij*. *X* is a + (*numSamples*, *numCols*) numpy array. *ij* is a list of tuples + (*i*, *j*). Each tuple is a pair of indexes into the columns of *X* + for which you want to compute coherence. For example, if *X* has 64 + columns, and you want to compute all nonredundant pairs, define *ij* + as:: ij = [] for i in range(64): for j in range(i+1,64): - ij.append( (i,j) ) + ij.append( (i, j) ) - The other function arguments, except for 'preferSpeedOverMemory' - (see below), are explained in the help string of 'psd'. + The other function arguments, except for *preferSpeedOverMemory* + (see below), are explained in the help string of :func:`psd`. - Return value is a tuple (Cxy, Phase, freqs). + Return value is a tuple (*Cxy*, *Phase*, *freqs*). - Cxy -- a dictionary of (i,j) tuples -> coherence vector for that - pair. Ie, Cxy[(i,j) = cohere(X[:,i], X[:,j]). Number of - dictionary keys is len(ij) + - *Cxy*: a dictionary of (*i*, *j*) tuples -> coherence vector for that + pair. I.e., ``Cxy[(i,j)] = cohere(X[:,i], X[:,j])``. Number of + dictionary keys is ``len(ij)``. - Phase -- a dictionary of phases of the cross spectral density at - each frequency for each pair. keys are (i,j). + - *Phase*: a dictionary of phases of the cross spectral density at + each frequency for each pair. The keys are ``(i,j)``. - freqs -- a vector of frequencies, equal in length to either the - coherence or phase vectors for any i,j key. Eg, to make a coherence - Bode plot: + - *freqs*: a vector of frequencies, equal in length to either + the coherence or phase vectors for any (*i*, *j*) key.. Eg, + to make a coherence Bode plot:: subplot(211) plot( freqs, Cxy[(12,19)]) subplot(212) plot( freqs, Phase[(12,19)]) - For a large number of pairs, cohere_pairs can be much more - efficient than just calling cohere for each pair, because it - caches most of the intensive computations. If N is the number of - pairs, this function is O(N) for most of the heavy lifting, - whereas calling cohere for each pair is O(N^2). However, because - of the caching, it is also more memory intensive, making 2 - additional complex arrays with approximately the same number of - elements as X. + For a large number of pairs, :func:`cohere_pairs` can be much more + efficient than just calling :func:`cohere` for each pair, because + it caches most of the intensive computations. If *N* is the + number of pairs, this function is O(N) for most of the heavy + lifting, whereas calling cohere for each pair is + O(N\N{SUPERSCRIPT TWO}). However, because of the caching, it is + also more memory intensive, making 2 additional complex arrays + with approximately the same number of elements as *X*. - The parameter 'preferSpeedOverMemory', if false, limits the + The parameter *preferSpeedOverMemory*, if *False*, limits the caching by only making one, rather than two, complex cache arrays. This is useful if memory becomes critical. Even when - preferSpeedOverMemory is false, cohere_pairs will still give - significant performace gains over calling cohere for each pair, - and will use subtantially less memory than if - preferSpeedOverMemory is true. In my tests with a 43000,64 array - over all nonredundant pairs, preferSpeedOverMemory=1 delivered a - 33% performace boost on a 1.7GHZ Athlon with 512MB RAM compared - with preferSpeedOverMemory=0. But both solutions were more than - 10x faster than naievly crunching all possible pairs through + *preferSpeedOverMemory* is *False*, :func:`cohere_pairs` will + still give significant performace gains over calling + :func:`cohere` for each pair, and will use subtantially less + memory than if *preferSpeedOverMemory* is *True*. In my tests + with a (43000, 64) array over all non-redundant pairs, + *preferSpeedOverMemory* = *True* delivered a 33% performace boost + on a 1.7GHZ Athlon with 512MB RAM compared with + *preferSpeedOverMemory* = *False*. But both solutions were more + than 10x faster than naievly crunching all possible pairs through cohere. .. seealso:: @@ -757,17 +767,21 @@ def entropy(y, bins): - """ - Return the entropy of the data in y + r""" + Return the entropy of the data in *y*. - \sum p_i log2(p_i) where p_i is the probability of observing y in - the ith bin of bins. bins can be a number of bins or a range of - bins; see numpy.histogram + .. math:: - Compare S with analytic calculation for a Gaussian - x = mu + sigma*randn(200000) - Sanalytic = 0.5 * ( 1.0 + log(2*pi*sigma**2.0) ) + \sum p_i \log_2(p_i) + where :math:`p_i` is the probability of observing *y* in the + :math:`i^{th}` bin of *bins*. *bins* can be a number of bins or a + range of bins; see :func:`numpy.histogram`. + + Compare *S* with analytic calculation for a Gaussian:: + + x = mu + sigma * randn(200000) + Sanalytic = 0.5 * ( 1.0 + log(2*pi*sigma**2.0) ) """ n,bins = np.histogram(y, bins) n = n.astype(np.float_) @@ -783,29 +797,30 @@ def hist(y, bins=10, normed=0): """ - Return the histogram of y with bins equally sized bins. If bins - is an array, use the bins. Return value is - (n,x) where n is the count for each bin in x + Return the histogram of *y* with *bins* equally sized bins. If + bins is an array, use those bins. Return value is (*n*, *x*) + where *n* is the count for each bin in *x*. - If normed is False, return the counts in the first element of the - return tuple. If normed is True, return the probability density - n/(len(y)*dbin) + If *normed* is *False*, return the counts in the first element of + the returned tuple. If *normed* is *True*, return the probability + density :math:`\\frac{n}{(len(y)\mathrm{dbin}}`. - If y has rank>1, it will be raveled. If y is masked, only - the unmasked values will be used. + If *y* has rank > 1, it will be raveled. If *y* is masked, only the + unmasked values will be used. + Credits: the Numeric 22 documentation """ warnings.warn("Use numpy.histogram()", DeprecationWarning) return np.histogram(y, bins=bins, range=None, normed=normed) def normpdf(x, *args): - "Return the normal pdf evaluated at x; args provides mu, sigma" + "Return the normal pdf evaluated at *x*; args provides *mu*, *sigma*" mu, sigma = args return 1./(np.sqrt(2*np.pi)*sigma)*np.exp(-0.5 * (1./sigma*(x - mu))**2) def levypdf(x, gamma, alpha): - "Returm the levy pdf evaluated at x for params gamma, alpha" + "Returm the levy pdf evaluated at *x* for params *gamma*, *alpha*" N = len(x) @@ -835,7 +850,7 @@ def trapz(x, y): """ - Trapezoidal integral of y(x). + Trapezoidal integral of *y*(*x*). """ warnings.warn("Use numpy.trapz(y,x) instead of trapz(x,y)", DeprecationWarning) return np.trapz(y, x) @@ -849,10 +864,9 @@ def longest_contiguous_ones(x): """ - return the indices of the longest stretch of contiguous ones in x, - assuming x is a vector of zeros and ones. - If there are two equally long stretches, pick the first - + Return the indices of the longest stretch of contiguous ones in *x*, + assuming *x* is a vector of zeros and ones. If there are two + equally long stretches, pick the first. """ x = np.ravel(x) if len(x)==0: @@ -880,16 +894,21 @@ def prepca(P, frac=0): """ - Compute the principal components of P. P is a numVars x - numObs array. frac is the minimum fraction of - variance that a component must contain to be included. + Compute the principal components of *P*. *P* is a (*numVars*, + *numObs*) array. *frac* is the minimum fraction of variance that a + component must contain to be included. - Return value are - Pcomponents : a numVars x numObs array - Trans : the weights matrix, ie, Pcomponents = Trans*P - fracVar : the fraction of the variance accounted for by each - component returned + Return value is a tuple of the form (*Pcomponents*, *Trans*, + *fracVar*) where: + - *Pcomponents* : a (numVars, numObs) array + + - *Trans* : the weights matrix, ie, *Pcomponents* = *Trans* * + *P* + + - *fracVar* : the fraction of the variance accounted for by each + component returned + A similar function of the same name was in the Matlab (TM) R13 Neural Network Toolbox but is not found in later versions; its successor seems to be called "processpcs". @@ -907,11 +926,11 @@ def prctile(x, p = (0.0, 25.0, 50.0, 75.0, 100.0)): """ - Return the percentiles of x. p can either be a sequence of - percentile values or a scalar. If p is a sequence the i-th element - of the return sequence is the p(i)-th percentile of x. - If p is a scalar, the largest value of x less than or equal - to the p percentage point in the sequence is returned. + Return the percentiles of *x*. *p* can either be a sequence of + percentile values or a scalar. If *p* is a sequence, the ith + element of the return sequence is the *p*(i)-th percentile of *x*. + If *p* is a scalar, the largest value of *x* less than or equal to + the *p* percentage point in the sequence is returned. """ @@ -929,15 +948,15 @@ def prctile_rank(x, p): """ - return the for each element in x, return the rank 0..len(p) . Eg - if p=(25, 50, 75), the return value will be a len(x) array with - values in [0,1,2,3] where 0 indicates the value is less than the - 25th percentile, 1 indicates the value is >= the 25th and < 50th - percentile, ... and 3 indicates the value is above the 75th - percentile cutoff + Return the rank for each element in *x*, return the rank + 0..len(*p*). Eg if *p* = (25, 50, 75), the return value will be a + len(*x*) array with values in [0,1,2,3] where 0 indicates the + value is less than the 25th percentile, 1 indicates the value is + >= the 25th and < 50th percentile, ... and 3 indicates the value + is above the 75th percentile cutoff. - p is either an array of percentiles in [0..100] or a scalar which - indicates how many quantiles of data you want ranked + *p* is either an array of percentiles in [0..100] or a scalar which + indicates how many quantiles of data you want ranked. """ if not cbook.iterable(p): @@ -953,10 +972,10 @@ def center_matrix(M, dim=0): """ - Return the matrix M with each row having zero mean and unit std + Return the matrix *M* with each row having zero mean and unit std. - if dim=1 operate on columns instead of rows. (dim is opposite - to the numpy axis kwarg.) + If *dim* = 1 operate on columns instead of rows. (*dim* is + opposite to the numpy axis kwarg.) """ M = np.asarray(M, np.float_) if dim: @@ -970,7 +989,10 @@ def rk4(derivs, y0, t): """ - Integrate 1D or ND system of ODEs using 4-th order Runge-Kutta. This is a toy implementation which may be useful if you find yourself stranded on a system w/o scipy. Otherwise use ``scipy.integrate`` + Integrate 1D or ND system of ODEs using 4-th order Runge-Kutta. + This is a toy implementation which may be useful if you find + yourself stranded on a system w/o scipy. Otherwise use + :func:`scipy.integrate`. *y0* initial state vector @@ -1009,7 +1031,6 @@ If you have access to scipy, you should probably be using the scipy.integrate tools rather than this function. - """ try: Ny = len(y0) @@ -1040,9 +1061,11 @@ def bivariate_normal(X, Y, sigmax=1.0, sigmay=1.0, mux=0.0, muy=0.0, sigmaxy=0.0): """ - Bivariate gaussan distribution for equal shape X, Y + Bivariate Gaussian distribution for equal shape *X*, *Y*. - See `bivariate normal <http://mathworld.wolfram.com/BivariateNormalDistribution.html>`_ at mathworld. + See `bivariate normal + <http://mathworld.wolfram.com/BivariateNormalDistribution.html>`_ + at mathworld. """ Xmu = X-mux Ymu = Y-muy @@ -1052,21 +1075,22 @@ denom = 2*np.pi*sigmax*sigmay*np.sqrt(1-rho**2) return np.exp( -z/(2*(1-rho**2))) / denom - - - def get_xyz_where(Z, Cond): """ - Z and Cond are MxN matrices. Z are data and Cond is a boolean - matrix where some condition is satisfied. Return value is x,y,z - where x and y are the indices into Z and z are the values of Z at - those indices. x,y,z are 1D arrays + *Z* and *Cond* are *M* x *N* matrices. *Z* are data and *Cond* is + a boolean matrix where some condition is satisfied. Return value + is (*x*, *y*, *z*) where *x* and *y* are the indices into *Z* and + *z* are the values of *Z* at those indices. *x*, *y*, and *z* are + 1D arrays. """ X,Y = np.indices(Z.shape) return X[Cond], Y[Cond], Z[Cond] def get_sparse_matrix(M,N,frac=0.1): - 'return a MxN sparse matrix with frac elements randomly filled' + """ + Return a *M* x *N* sparse matrix with *frac* elements randomly + filled. + """ data = np.zeros((M,N))*0. for i in range(int(M*N*frac)): x = np.random.randint(0,M-1) @@ -1075,15 +1099,17 @@ return data def dist(x,y): - 'return the distance between two points' + """ + Return the distance between two points. + """ d = x-y return np.sqrt(np.dot(d,d)) def dist_point_to_segment(p, s0, s1): """ - get the distance of a point to a segment. + Get the distance of a point to a segment. - p, s0, s1 are xy sequences + *p*, *s0*, *s1* are *xy* sequences This algorithm from http://softsurfer.com/Archive/algorithm_0102/algorithm_0102.htm#Distance%20to%20Ray%20or%20Segment @@ -1108,12 +1134,11 @@ def segments_intersect(s1, s2): """ - Return True if s1 and s2 intersect. - s1 and s2 are defined as + Return *True* if *s1* and *s2* intersect. + *s1* and *s2* are defined as:: - s1: (x1, y1), (x2, y2) - s2: (x3, y3), (x4, y4) - + s1: (x1, y1), (x2, y2) + s2: (x3, y3), (x4, y4) """ (x1, y1), (x2, y2) = s1 (x3, y3), (x4, y4) = s2 @@ -1135,7 +1160,7 @@ def fftsurr(x, detrend=detrend_none, window=window_none): """ - Compute an FFT phase randomized surrogate of x + Compute an FFT phase randomized surrogate of *x*. """ if cbook.iterable(window): x=window*detrend(x) @@ -1168,30 +1193,36 @@ What the function here calculates may not be what you really want; *caveat emptor*. - It also seems that this function's name is badly misspelled. + It also seems that this function's name is badly misspelled. """ return np.mean(np.log(np.absolute(fprime(x)))) class FIFOBuffer: """ - A FIFO queue to hold incoming x, y data in a rotating buffer using - numpy arrays under the hood. It is assumed that you will call - asarrays much less frequently than you add data to the queue -- - otherwise another data structure will be faster + A FIFO queue to hold incoming *x*, *y* data in a rotating buffer + using numpy arrays under the hood. It is assumed that you will + call asarrays much less frequently than you add data to the queue + -- otherwise another data structure will be faster. This can be used to support plots where data is added from a real - time feed and the plot object wants grab data from the buffer and - plot it to screen less freqeuently than the incoming + time feed and the plot object wants to grab data from the buffer + and plot it to screen less freqeuently than the incoming. - If you set the dataLim attr to a matplotlib BBox (eg ax.dataLim), - the dataLim will be updated as new data come in + If you set the *dataLim* attr to + :class:`~matplotlib.transforms.BBox` (eg + :attr:`matplotlib.Axes.dataLim`), the *dataLim* will be updated as + new data come in. - TODI: add a grow method that will extend nmax + TODO: add a grow method that will extend nmax - mlab seems like the wrong place for this class. + .. note:: + + mlab seems like the wrong place for this class. """ def __init__(self, nmax): - 'buffer up to nmax points' + """ + Buffer up to *nmax* points. + """ self._xa = np.zeros((nmax,), np.float_) self._ya = np.zeros((nmax,), np.float_) self._xs = np.zeros((nmax,), np.float_) @@ -1202,11 +1233,16 @@ self.callbackd = {} def register(self, func, N): - 'call func everytime N events are passed; func signature is func(fifo)' + """ + Call *func* every time *N* events are passed; *func* signature + is ``func(fifo)``. + """ self.callbackd.setdefault(N, []).append(func) def add(self, x, y): - 'add scalar x and y to the queue' + """ + Add scalar *x* and *y* to the queue. + """ if self.dataLim is not None: xys = ((x,y),) self.dataLim.update(xys, -1) #-1 means use the default ignore setting @@ -1223,15 +1259,17 @@ self._ind += 1 def last(self): - 'get the last x, y or None, None if no data set' + """ + Get the last *x*, *y* or *None*. *None* if no data set. + """ if self._ind==0: return None, None ind = (self._ind-1) % self._nmax return self._xs[ind], self._ys[ind] def asarrays(self): """ - return x and y as arrays; their length will be the len of data - added or nmax + Return *x* and *y* as arrays; their length will be the len of + data added or *nmax*. """ if self._ind<self._nmax: return self._xs[:self._ind], self._ys[:self._ind] @@ -1245,36 +1283,41 @@ return self._xa, self._ya def update_datalim_to_current(self): - 'update the datalim in the current data in the fifo' + """ + Update the *datalim* in the current data in the fifo. + """ if self.dataLim is None: raise ValueError('You must first set the dataLim attr') x, y = self.asarrays() self.dataLim.update_numerix(x, y, True) def movavg(x,n): - 'compute the... [truncated message content] |
From: <md...@us...> - 2008-10-28 18:11:02
|
Revision: 6342 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6342&view=rev Author: mdboom Date: 2008-10-28 18:10:51 +0000 (Tue, 28 Oct 2008) Log Message: ----------- Add nearest neighbor search for fonts. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/fonts_demo.py trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py trunk/matplotlib/lib/matplotlib/font_manager.py trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py trunk/matplotlib/lib/matplotlib/text.py Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -11,7 +11,7 @@ subplot(111, axisbg='w') font0 = FontProperties() -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -53,7 +53,7 @@ t = text(0.0, 0.9, 'variant', fontproperties=font1, **alignment) -for k in range(1): +for k in range(2): font = font0.copy() font.set_family('serif') font.set_variant(variant[k]) Modified: trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/examples/pylab_examples/fonts_demo_kw.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -8,7 +8,7 @@ from pylab import * subplot(111, axisbg='w') -alignment = {'horizontalalignment':'center', 'verticalalignment':'center'} +alignment = {'horizontalalignment':'center', 'verticalalignment':'baseline'} ### Show family options family = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] @@ -40,7 +40,7 @@ t = text(0.0, 0.9, 'variant', **alignment) -for k in range(1): +for k in range(2): t = text( 0.0, yp[k], variant[k], family='serif', variant=variant[k], **alignment) Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -4,9 +4,9 @@ This module provides a single :class:`FontManager` instance that can be shared across backends and platforms. The :func:`findfont` function returns the best TrueType (TTF) font file in the local or -system font path that matches the specified FontProperties. The -FontManager also handles Adobe Font Metrics (AFM) font files for use -by the PostScript backend. +system font path that matches the specified :class:`FontProperties` +instance. The :class:`FontManager` also handles Adobe Font Metrics +(AFM) font files for use by the PostScript backend. The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) font specification <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_. @@ -64,15 +64,53 @@ verbose = matplotlib.verbose -font_scalings = {'xx-small': 0.579, 'x-small': 0.694, 'small': 0.833, - 'medium': 1.0, 'large': 1.200, 'x-large': 1.440, - 'xx-large': 1.728, 'larger': 1.2, 'smaller': 0.833} +font_scalings = { + 'xx-small' : 0.579, + 'x-small' : 0.694, + 'small' : 0.833, + 'medium' : 1.0, + 'large' : 1.200, + 'x-large' : 1.440, + 'xx-large' : 1.728, + 'larger' : 1.2, + 'smaller' : 0.833, + None : 1.0} -weight_dict = {'light': 200, 'normal': 400, 'regular': 400, 'book': 400, - 'medium': 500, 'roman': 500, 'semibold': 600, 'demibold': 600, - 'demi': 600, 'bold': 700, 'heavy': 800, 'extra bold': 800, - 'black': 900} +stretch_dict = { + 'ultra-condensed' : 100, + 'extra-condensed' : 200, + 'condensed' : 300, + 'semi-condensed' : 400, + 'normal' : 500, + 'semi-expanded' : 600, + 'expanded' : 700, + 'extra-expanded' : 800, + 'ultra-expanded' : 900} +weight_dict = { + 'ultralight' : 100, + 'light' : 200, + 'normal' : 400, + 'regular' : 400, + 'book' : 400, + 'medium' : 500, + 'roman' : 500, + 'semibold' : 600, + 'demibold' : 600, + 'demi' : 600, + 'bold' : 700, + 'heavy' : 800, + 'extra bold' : 800, + 'black' : 900} + +font_family_aliases = set([ + 'serif', + 'sans-serif', + 'cursive', + 'fantasy', + 'monospace', + 'sans']) + # OS Font paths MSFolders = \ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' @@ -117,12 +155,14 @@ 'afm': ('afm',)}[fontext] def win32FontDirectory(): - """Return the user-specified font directory for Win32. This is + """ + Return the user-specified font directory for Win32. This is looked up from the registry key:: \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts - If the key is not found, $WINDIR/Fonts will be returned.""" + If the key is not found, $WINDIR/Fonts will be returned. + """ try: import _winreg except ImportError: @@ -331,19 +371,21 @@ return weight -class FontKey(object): +class FontEntry(object): """ A class for storing Font properties. It is used when populating the font lookup dictionary. """ def __init__(self, + fname ='', name ='', style ='normal', variant='normal', weight ='normal', stretch='normal', - size ='medium' + size ='medium', ): + self.fname = fname self.name = name self.style = style self.variant = variant @@ -355,12 +397,12 @@ self.size = size -def ttfFontProperty(font): +def ttfFontProperty(fontpath, font): """ A function for populating the :class:`FontKey` by extracting information from the TrueType font file. - *font* is an :class:`FT2Font` instance. + *font* is a :class:`FT2Font` instance. """ name = font.family_name @@ -447,10 +489,10 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def afmFontProperty(font): +def afmFontProperty(fontpath, font): """ A function for populating a :class:`FontKey` instance by extracting information from the AFM font file. @@ -507,37 +549,17 @@ # !!!! Incomplete size_adjust = None - return FontKey(name, style, variant, weight, stretch, size) + return FontEntry(fontpath, name, style, variant, weight, stretch, size) -def add_filename(fontdict, prop, fname): +def createFontList(fontfiles, fontext='ttf'): """ - A function to add a font file name to the font dictionary using - the :class:`FontKey` properties. If a font property has no - dictionary, then create it. + A function to create a font lookup list. The default is to create + a list of TrueType fonts. An AFM font list can optionally be + created. """ - try: - size = str(float(prop.size)) - except ValueError: - size = prop.size - d = fontdict. \ - setdefault(prop.name, {}).\ - setdefault(prop.style, {}).\ - setdefault(prop.variant, {}).\ - setdefault(prop.weight, {}).\ - setdefault(prop.stretch, {}) - d[size] = fname - - -def createFontDict(fontfiles, fontext='ttf'): - """ - A function to create a font lookup dictionary. The default is to - create a dictionary for TrueType fonts. An AFM font dictionary - can optionally be created. - """ - - fontdict = {} + fontlist = [] # Add fonts from list of known font files. seen = {} for fpath in fontfiles: @@ -559,7 +581,7 @@ except RuntimeError: verbose.report("Could not parse font file %s"%fpath) continue - prop = afmFontProperty(font) + prop = afmFontProperty(fpath, font) else: try: font = ft2font.FT2Font(str(fpath)) @@ -570,59 +592,12 @@ verbose.report("Cannot handle unicode filenames") #print >> sys.stderr, 'Bad file is', fpath continue - try: prop = ttfFontProperty(font) + try: prop = ttfFontProperty(fpath, font) except: continue - add_filename(fontdict, prop, fpath) - return fontdict + fontlist.append(prop) + return fontlist -def setWeights(font): - """ - A function to populate missing values in a font weight - dictionary. This proceedure is necessary since the font finding - algorithm always matches on the weight property. - """ - - # !!!! Not completely correct - temp = font.copy() - if len(temp) == 1: - wgt = temp.keys()[0] - for j in range(100, 1000, 100): - font[j] = temp[wgt] - - if 400 in temp: - for j in range(100, 1000, 100): - font[j] = temp[400] - if 500 in temp: - if 400 in temp: - for j in range(500, 1000, 100): - font[j] = temp[500] - else: - for j in range(100, 1000, 100): - font[j] = temp[500] - - if 300 in temp: - for j in [100, 200, 300]: - font[j] = temp[300] - if 200 in temp: - if 300 in temp: - for j in [100, 200]: - font[j] = temp[200] - else: - for j in [100, 200, 300]: - font[j] = temp[200] - - if 800 in temp: - for j in [600, 700, 800, 900]: - font[j] = temp[800] - if 700 in temp: - if 800 in temp: - for j in [600, 700]: - font[j] = temp[700] - else: - for j in [600, 700, 800, 900]: - font[j] = temp[700] - class FontProperties(object): """ A class for storing and manipulating font properties. @@ -633,31 +608,33 @@ specification. The six properties are: - family: A list of font names in decreasing order of priority. - The last item is the default font name and is given the name - of the font family, either 'serif', 'sans-serif', 'cursive', - 'fantasy', or 'monospace'. + The items may include a generic font family name, either + 'serif', 'sans-serif', 'cursive', 'fantasy', or 'monospace'. + In that case, the actual font to be used will be looked up + from the associated rcParam in :file:`matplotlibrc`. - style: Either 'normal', 'italic' or 'oblique'. - variant: Either 'normal' or 'small-caps'. - - stretch: Either an absolute value of 'ultra-condensed', - 'extra- condensed', 'condensed', 'semi-condensed', 'normal', - 'semi-expanded', 'expanded', 'extra-expanded' or - 'ultra-expanded'; or a relative value of narrower or wider. - This property is currently not implemented and is set to - normal. + - stretch: A numeric value in the range 0-1000 or one of + 'ultra-condensed', 'extra-condensed', 'condensed', + 'semi-condensed', 'normal', 'semi-expanded', 'expanded', + 'extra-expanded' or 'ultra-expanded' - - weight: A numeric value in the range 100, 200, 300, ..., 900. + - weight: A numeric value in the range 0-1000 or one of + 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', + 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', + 'extra bold', 'black' - - size: Either an absolute value of 'xx-small', 'x-small', 'small', - 'medium', 'large', 'x-large', 'xx-large'; or a relative value - of smaller or larger; or an absolute font size, e.g. 12; - or 'scalable'. + - size: Either an relative value of 'xx-small', 'x-small', + 'small', 'medium', 'large', 'x-large', 'xx-large' or an + absolute font size, e.g. 12 - The default font property for TrueType fonts is:: + The default font property for TrueType fonts (as specified in the + default :file:`matplotlibrc` file) is:: - sans-serif, normal, normal, normal, 400, scalable. + sans-serif, normal, normal, normal, normal, scalable. Alternatively, a font may be specified using an absolute path to a .ttf file, by using the *fname* kwarg. @@ -672,9 +649,8 @@ <http://www.fontconfig.org/>`_ pattern, if it is the only argument provided. See the documentation on `fontconfig patterns <http://www.fontconfig.org/fontconfig-user.html>`_. This support - does not require fontconfig to be installed or support for it to - be enabled. We are merely borrowing its pattern syntax for use - here. + does not require fontconfig to be installed. We are merely + borrowing its pattern syntax for use here. Note that matplotlib's internal font manager and fontconfig use a different algorithm to lookup fonts, so the results of the same pattern @@ -760,6 +736,7 @@ if self._slant is None: return rcParams['font.style'] return self._slant + get_slant = get_style def get_variant(self): """ @@ -772,7 +749,10 @@ def get_weight(self): """ - Return the font weight. + Set the font weight. Options are: A numeric value in the + range 0-1000 or one of 'light', 'normal', 'regular', 'book', + 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', + 'heavy', 'extra bold', 'black' """ if self._weight is None: return rcParams['font.weight'] @@ -780,8 +760,9 @@ def get_stretch(self): """ - Return the font stretch or width. Options are: 'normal', - 'narrow', 'condensed', or 'wide'. + Return the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'. """ if self._stretch is None: return rcParams['font.stretch'] @@ -793,8 +774,17 @@ """ if self._size is None: return rcParams['font.size'] - return float(self._size) + return self._size + def get_size_in_points(self): + if self._size is not None: + try: + return float(self._size) + except ValueError: + pass + default_size = fontManager.get_default_size() + return default_size * font_scalings.get(self._size) + def get_file(self): """ Return the filename of the associated font. @@ -849,46 +839,51 @@ def set_weight(self, weight): """ - Set the font weight. + Set the font weight. May be either a numeric value in the + range 0-1000 or one of 'ultralight', 'light', 'normal', + 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', + 'demi', 'bold', 'heavy', 'extra bold', 'black' """ - if (weight is not None and - weight not in weight_dict and - weight not in weight_dict.keys()): - raise ValueError("weight is invalid") + if weight is not None: + try: + weight = int(weight) + if weight < 0 or weight > 1000: + raise ValueError() + except ValueError: + if weight not in weight_dict: + raise ValueError("weight is invalid") self._weight = weight def set_stretch(self, stretch): """ - Set the font stretch or width. Options are: 'normal', 'narrow', - 'condensed', or 'wide'. + Set the font stretch or width. Options are: 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', 'normal', + 'semi-expanded', 'expanded', 'extra-expanded' or + 'ultra-expanded', or a numeric value in the range 0-1000. """ - if stretch not in ('normal', 'narrow', 'condensed', 'wide', None): - raise ValueError("stretch is invalid") + if stretch is not None: + try: + stretch = int(stretch) + if stretch < 0 or stretch > 1000: + raise ValueError() + except ValueError: + if stretch not in stretch_dict: + raise ValueError("stretch is invalid") self._stretch = stretch def set_size(self, size): """ - Set the font size. Either an absolute value of 'xx-small', - 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'; - or a relative value of smaller or larger; or an absolute font - size, e.g. 12; or 'scalable'. + Set the font size. Either an relative value of 'xx-small', + 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large' + or an absolute font size, e.g. 12. """ - if size is None: - self._size = None - else: - if is_string_like(size): - parent_size = fontManager.get_default_size() - scaling = font_scalings.get(size) - if scaling is not None: - size = parent_size * scaling - else: - try: - size = float(size) - except ValueError: - size = parent_size - assert(type(size) in (int, float)) - self._size = size - get_size_in_points = get_size + if size is not None: + try: + size = float(size) + except ValueError: + if size is not None and size not in font_scalings: + raise ValueError("size is invalid") + self._size = size def set_file(self, file): """ @@ -958,24 +953,18 @@ class FontManager: """ On import, the :class:`FontManager` singleton instance creates a - dictionary of TrueType fonts based on the font properties: name, - style, variant, weight, stretch, and size. The :meth:`findfont` - method searches this dictionary for a font file name that exactly - matches the font properties of the specified text. If none is - found, a default font is returned. By updating the dictionary - with the properties of the found font, the font dictionary can act - like a font cache. - - .. note:: The font lookup mechanism is very exact and brittle. If - the *exact* font specified is not found, a default font is - always returned. This should be improved in the future. + list of TrueType fonts based on the font properties: name, style, + variant, weight, stretch, and size. The :meth:`findfont` method + does a nearest neighbor search to find the font that most closely + matches the specification. If no good enough match is found, a + default font is returned. """ def __init__(self, size=None, weight='normal'): self.__default_weight = weight self.default_size = size - paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), - os.path.join(rcParams['datapath'],'fonts','afm')] + paths = [os.path.join(rcParams['datapath'], 'fonts', 'ttf'), + os.path.join(rcParams['datapath'], 'fonts', 'afm')] # Create list of font paths for pathname in ['TTFPATH', 'AFMPATH']: @@ -1002,7 +991,7 @@ # use anything self.defaultFont = self.ttffiles[0] - self.ttfdict = createFontDict(self.ttffiles) + self.ttflist = createFontList(self.ttffiles) if rcParams['pdf.use14corefonts']: # Load only the 14 PDF core fonts. These fonts do not need to be @@ -1013,12 +1002,15 @@ # ZapfDingbats. afmpath = os.path.join(rcParams['datapath'],'fonts','pdfcorefonts') afmfiles = findSystemFonts(afmpath, fontext='afm') - self.afmdict = createFontDict(afmfiles, fontext='afm') + self.afmlist = createFontList(afmfiles, fontext='afm') else: self.afmfiles = findSystemFonts(paths, fontext='afm') + \ findSystemFonts(fontext='afm') - self.afmdict = createFontDict(self.afmfiles, fontext='afm') + self.afmlist = createFontList(self.afmfiles, fontext='afm') + self.ttf_lookup_cache = {} + self.afm_lookup_cache = {} + def get_default_weight(self): """ Return the default font weight. @@ -1054,17 +1046,133 @@ # !!!! Needs implementing raise NotImplementedError + # Each of the scoring functions below should return a value between + # 0.0 (perfect match) and 1.0 (terrible match) + def score_family(self, families, family2): + """ + Returns a match score between the list of font families in + *families* and the font family name *family2*. + + An exact match anywhere in the list returns 0.0. + + A match by generic font name will return 0.1. + + No match will return 1.0. + """ + for i, family1 in enumerate(families): + if family1.lower() in font_family_aliases: + if family1 == 'sans': + family1 == 'sans-serif' + options = rcParams['font.' + family1] + if family2 in options: + idx = options.index(family2) + return 0.1 + elif family1.lower() == family2.lower(): + return 0.0 + return 1.0 + + def score_style(self, style1, style2): + """ + Returns a match score between *style1* and *style2*. + + An exact match returns 0.0. + + A match between 'italic' and 'oblique' returns 0.1. + + No match returns 1.0. + """ + if style1 == style2: + return 0.0 + elif style1 in ('italic', 'oblique') and \ + style2 in ('italic', 'oblique'): + return 0.1 + return 1.0 + + def score_variant(self, variant1, variant2): + """ + Returns a match score between *variant1* and *variant2*. + + An exact match returns 0.0, otherwise 1.0. + """ + if variant1 == variant2: + return 0.0 + else: + return 1.0 + + def score_stretch(self, stretch1, stretch2): + """ + Returns a match score between *stretch1* and *stretch2*. + + The result is the absolute value of the difference between the + CSS numeric values of *stretch1* and *stretch2*, normalized + between 0.0 and 1.0. + """ + try: + stretchval1 = int(stretch1) + except ValueError: + stretchval1 = stretch_dict.get(stretch1, 500) + try: + stretchval2 = int(stretch2) + except ValueError: + stretchval2 = stretch_dict.get(stretch2, 500) + return abs(stretchval1 - stretchval2) / 1000.0 + + def score_weight(self, weight1, weight2): + """ + Returns a match score between *weight1* and *weight2*. + + The result is the absolute value of the difference between the + CSS numeric values of *weight1* and *weight2*, normalized + between 0.0 and 1.0. + """ + try: + weightval1 = int(weight1) + except ValueError: + weightval1 = weight_dict.get(weight1, 500) + try: + weightval2 = int(weight2) + except ValueError: + weightval2 = weight_dict.get(weight2, 500) + return abs(weightval1 - weightval2) / 1000.0 + + def score_size(self, size1, size2): + """ + Returns a match score between *size1* and *size2*. + + If *size2* (the size specified in the font file) is 'scalable', this + function always returns 0.0, since any font size can be generated. + + Otherwise, the result is the absolute distance between *size1* and + *size2*, normalized so that the usual range of font sizes (6pt - + 72pt) will lie between 0.0 and 1.0. + """ + if size2 == 'scalable': + return 0.0 + # Size value should have already been + try: + sizeval1 = float(size1) + except ValueError: + sizeval1 = self.default_size * font_scalings(size1) + try: + sizeval2 = float(size2) + except ValueError: + return 1.0 + return abs(sizeval1 - sizeval2) / 72.0 + def findfont(self, prop, fontext='ttf'): """ - Search the font dictionary for a font that exactly or closely - matches the specified font properties. See the - :class:`FontProperties` class for a description. + Search the font list for the font that most closely matches + the :class:`FontProperties` *prop*. - The properties are searched in the following order: name, - style, variant, weight, stretch, and size. The font weight - always matches returning the closest weight, and the font size - always matches for scalable fonts. An oblique style font will - be used inplace of a missing italic style font if present. + :meth:`findfont` performs a nearest neighbor search. Each + font is given a similarity score to the target font + properties. The first font with the highest score is + returned. If no matches below a certain threshold are found, + the default font (usually Vera Sans) is returned. + + The result is cached, so subsequent lookups don't have to + perform the O(n) nearest neighbor search. + See the `W3C Cascading Style Sheet, Level 1 <http://www.w3.org/TR/1998/REC-CSS2-19980512/>`_ documentation for a description of the font finding algorithm. @@ -1078,108 +1186,45 @@ return fname if fontext == 'afm': - fontdict = self.afmdict + font_cache = self.afm_lookup_cache + fontlist = self.afmlist else: - fontdict = self.ttfdict + font_cache = self.ttf_lookup_cache + fontlist = self.ttflist - original_name = prop.get_family()[0] - style = prop.get_style() - variant = prop.get_variant() - weight = weight_as_number(prop.get_weight()) - stretch = prop.get_stretch() - size = str(prop.get_size_in_points()) + cached = font_cache.get(prop) + if cached: + return cached - def lookup_name(name): - try: - fname = fontdict[name][style][variant][weight][stretch][size] - verbose.report('\tfindfont cached %(name)s, %(style)s, %(variant)s, %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - except KeyError: - pass + best_score = 1e64 + best_font = None + for font in fontlist: + # Matching family should have highest priority, so it is multiplied + # by 10.0 + score = \ + self.score_family(prop.get_family(), font.name) * 10.0 + \ + self.score_style(prop.get_style(), font.style) + \ + self.score_variant(prop.get_variant(), font.variant) + \ + self.score_weight(prop.get_weight(), font.weight) + \ + self.score_stretch(prop.get_stretch(), font.stretch) + \ + self.score_size(prop.get_size(), font.size) + if score < best_score: + best_score = score + best_font = font - fname = None - font = fontdict - if name in font: - font = font[name] - else: - verbose.report('\tfindfont failed %(name)s'%locals(), 'debug') - return None + if best_font is None or best_score > 10.0: + verbose.report('findfont: Could not match %s. Returning %s' % + (prop, self.defaultFont)) + result = self.defaultFont + else: + verbose.report('findfont: Matching %s to %s (%s) with score of %f' % + (prop, best_font.name, best_font.fname, best_score)) + result = best_font.fname - if style in font: - font = font[style] - elif style == 'italic' and 'oblique' in font: - font = font['oblique'] - elif style == 'oblique' and 'italic' in font: - font = font['italic'] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s'%locals(), 'debug') - return None + font_cache[prop] = result + return result - if variant in font: - font = font[variant] - else: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s'%locals(), 'debug') - return None - if weight not in font: - setWeights(font) - if weight not in font: - return None - font = font[weight] - - if stretch in font: - stretch_font = font[stretch] - if 'scalable' in stretch_font: - fname = stretch_font['scalable'] - elif size in stretch_font: - fname = stretch_font[size] - - if fname is None: - for val in font.values(): - if 'scalable' in val: - fname = val['scalable'] - break - - if fname is None: - for val in font.values(): - if size in val: - fname = val[size] - break - - if fname is None: - verbose.report('\tfindfont failed %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s'%locals(), 'debug') - else: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, fname) - verbose.report('\tfindfont found %(name)s, %(style)s, %(variant)s %(weight)s, %(stretch)s, %(size)s'%locals(), 'debug') - verbose.report('findfont returning %s'%fname, 'debug') - return fname - - font_family_aliases = set(['serif', 'sans-serif', 'cursive', - 'fantasy', 'monospace', 'sans']) - - for name in prop.get_family(): - if name in font_family_aliases: - if name == 'sans': - name = 'sans-serif' - for name2 in rcParams['font.' + name]: - fname = lookup_name(name2) - if fname: - break - else: - fname = lookup_name(name) - if fname: - break - - if not fname: - fontkey = FontKey(",".join(prop.get_family()), style, variant, weight, stretch, size) - add_filename(fontdict, fontkey, self.defaultFont) - verbose.report('Could not match %s, %s, %s. Returning %s' % (name, style, weight, self.defaultFont)) - return self.defaultFont - return fname - - _is_opentype_cff_font_cache = {} def is_opentype_cff_font(filename): """ @@ -1232,7 +1277,7 @@ return result else: - _fmcache = os.path.join(get_configdir(), 'fontManager.cache') + _fmcache = os.path.join(get_configdir(), 'fontList.cache') fontManager = None Modified: trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py =================================================================== --- trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/fontconfig_pattern.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -178,5 +178,4 @@ if val != []: val = ','.join(val) props.append(":%s=%s" % (key, val)) - print parse_fontconfig_pattern(''.join(props)) return ''.join(props) Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 12:51:39 UTC (rev 6341) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-10-28 18:10:51 UTC (rev 6342) @@ -445,9 +445,17 @@ "Return the :class:`~font_manager.FontProperties` object" return self._fontproperties + def get_family(self): + "Return the list of font families used for font lookup" + return self._fontproperties.get_family() + + def get_fontfamily(self): + 'alias for get_family' + return self.get_family() + def get_name(self): "Return the font name as string" - return self._fontproperties.get_family()[-1] # temporary hack. + return self._fontproperties.get_name() def get_style(self): "Return the font style as string" @@ -457,26 +465,42 @@ "Return the font size as integer" return self._fontproperties.get_size_in_points() + def get_variant(self): + "Return the font variant as a string" + return self._fontproperties.get_variant() + + def get_fontvariant(self): + 'alias for get_variant' + return self.get_variant() + def get_weight(self): - "Get the font weight as string" + "Get the font weight as string or number" return self._fontproperties.get_weight() def get_fontname(self): 'alias for get_name' - return self._fontproperties.get_family()[-1] # temporary hack. + return self.get_name() def get_fontstyle(self): 'alias for get_style' - return self._fontproperties.get_style() + return self.get_style() def get_fontsize(self): 'alias for get_size' - return self._fontproperties.get_size_in_points() + return self.get_size() def get_fontweight(self): 'alias for get_weight' - return self._fontproperties.get_weight() + return self.get_weight() + def get_stretch(self): + 'Get the font stretch as a string or number' + return self._fontproperties.get_stretch() + + def get_fontstretch(self): + 'alias for get_stretch' + return self.get_stretch() + def get_ha(self): 'alias for get_horizontalalignment' return self.get_horizontalalignment() @@ -645,35 +669,35 @@ def set_family(self, fontname): """ - Set the font family + Set the font family. May be either a single string, or a list + of strings in decreasing priority. Each string may be either + a real font name or a generic font class name. If the latter, + the specific font names will be looked up in the + :file:`matplotlibrc` file. - ACCEPTS: [ 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] + ACCEPTS: [ FONTNAME | 'serif' | 'sans-serif' | 'cursive' | 'fantasy' | 'monospace' ] """ self._fontproperties.set_family(fontname) def set_variant(self, variant): """ - Set the font variant, eg, + Set the font variant, either 'normal' or 'small-caps'. ACCEPTS: [ 'normal' | 'small-caps' ] """ self._fontproperties.set_variant(variant) def set_name(self, fontname): - """ - Set the font name, + """alias for set_family""" + return self.set_family(fontname) - ACCEPTS: string eg, ['Sans' | 'Courier' | 'Helvetica' ...] - """ - self._fontproperties.set_family(fontname) - def set_fontname(self, fontname): - 'alias for set_name' + """alias for set_family""" self.set_family(fontname) def set_style(self, fontstyle): """ - Set the font style + Set the font style. ACCEPTS: [ 'normal' | 'italic' | 'oblique'] """ @@ -681,33 +705,45 @@ def set_fontstyle(self, fontstyle): 'alias for set_style' - self._fontproperties.set_style(fontstyle) + return self.set_style(fontstyle) def set_size(self, fontsize): """ - Set the font size, eg., 8, 10, 12, 14... + Set the font size. May be either a size string, relative to + the default font size, or an absolute font size in points. - ACCEPTS: [ size in points | relative size eg 'smaller', 'x-large' ] + ACCEPTS: [ size in points | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' ] """ self._fontproperties.set_size(fontsize) def set_fontsize(self, fontsize): 'alias for set_size' - self._fontproperties.set_size(fontsize) + return self.set_size(fontsize) + def set_weight(self, weight): + """ + Set the font weight. + + ACCEPTS: [ a numeric value in range 0-1000 | 'ultralight' | 'light' | 'normal' | 'regular' | 'book' | 'medium' | 'roman' | 'semibold' | 'demibold' | 'demi' | 'bold' | 'heavy' | 'extra bold' | 'black' ] + """ + self._fontproperties.set_weight(weight) + def set_fontweight(self, weight): 'alias for set_weight' - self._fontproperties.set_weight(weight) + return self.set_weight(weight) - def set_weight(self, weight): + def set_stretch(self, stretch): """ - Set the font weight + Set the font stretch (horizontal condensation or expansion). - ACCEPTS: [ 'normal' | 'bold' | 'heavy' | 'light' | 'ultrabold' | - 'ultralight'] + ACCEPTS: [ a numeric value in range 0-1000 | 'ultra-condensed' | 'extra-condensed' | 'condensed' | 'semi-condensed' | 'normal' | 'semi-expanded' | 'expanded' | 'extra-expanded' | 'ultra-expanded' ] """ - self._fontproperties.set_weight(weight) + self._fontproperties.set_stretch(stretch) + def set_fontstretch(self, stretch): + 'alias for set_stretch' + return self.set_stretch(stretch) + def set_position(self, xy): """ Set the (*x*, *y*) position of the text This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-10-30 13:18:15
|
Revision: 6353 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6353&view=rev Author: jdh2358 Date: 2008-10-30 13:18:07 +0000 (Thu, 30 Oct 2008) Log Message: ----------- added jae-joons fancy arrow and box patch for annotations Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/text.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py trunk/matplotlib/lib/matplotlib/bezier.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-10-29 20:28:57 UTC (rev 6352) +++ trunk/matplotlib/CHANGELOG 2008-10-30 13:18:07 UTC (rev 6353) @@ -1,3 +1,7 @@ +2008-10-24 Added Jae Joon's fancy arrow, box and annotation + enhancements -- see + examples/pylab_examples/annotation_demo2.py + 2008-10-23 Autoscaling is now supported with shared axes - EF 2008-10-23 Fixed exception in dviread that happened with Minion - JKS Added: trunk/matplotlib/examples/pylab_examples/annotation_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -0,0 +1,151 @@ + +from matplotlib.pyplot import figure, show +from matplotlib.patches import Ellipse +import numpy as np + +if 1: + fig = figure(1,figsize=(8,5)) + ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1,5), ylim=(-4,3)) + + t = np.arange(0.0, 5.0, 0.01) + s = np.cos(2*np.pi*t) + line, = ax.plot(t, s, lw=3, color='purple') + + ax.annotate('arrowstyle', xy=(0, 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->") + ) + + ax.annotate('arc3', xy=(0.5, -1), xycoords='data', + xytext=(-30, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc3,rad=.2") + ) + + ax.annotate('arc', xy=(1., 1), xycoords='data', + xytext=(-40, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc,angleA=0,armA=30,rad=10"), + ) + + ax.annotate('arc', xy=(1.5, -1), xycoords='data', + xytext=(-40, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="arc,angleA=0,armA=20,angleB=-90,armB=15,rad=7"), + ) + + ax.annotate('angle', xy=(2., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + ax.annotate('angle3', xy=(2.5, -1), xycoords='data', + xytext=(-50, -30), textcoords='offset points', + arrowprops=dict(arrowstyle="->", + connectionstyle="angle3,angleA=0,angleB=-90"), + ) + + + ax.annotate('angle', xy=(3., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + ax.annotate('angle', xy=(3.5, -1), xycoords='data', + xytext=(-70, -60), textcoords='offset points', + size=20, + bbox=dict(boxstyle="round4,pad=.5", fc="0.8"), + arrowprops=dict(arrowstyle="->", + connectionstyle="angle,angleA=0,angleB=-90,rad=10"), + ) + + ax.annotate('angle', xy=(4., 1), xycoords='data', + xytext=(-50, 30), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + shrinkA=0, shrinkB=10, + connectionstyle="angle,angleA=0,angleB=90,rad=10"), + ) + + + + fig.savefig('annotation_connection') + + +if 1: + fig = figure(2) + fig.clf() + ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1,5), ylim=(-5,3)) + + el = Ellipse((2, -1), 0.5, 0.5) + ax.add_patch(el) + + ax.annotate('$->$', xy=(2., -1), xycoords='data', + xytext=(-150, -140), textcoords='offset points', + bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="->", + patchB=el, + connectionstyle="angle,angleA=90,angleB=0,rad=10"), + ) + + ax.annotate('fancy', xy=(2., -1), xycoords='data', + xytext=(-100, 60), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="fancy", + fc="0.6", ec="none", + patchB=el, + connectionstyle="angle3,angleA=0,angleB=-90"), + ) + + ax.annotate('simple', xy=(2., -1), xycoords='data', + xytext=(100, 60), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="simple", + fc="0.6", ec="none", + patchB=el, + connectionstyle="arc3,rad=0.3"), + ) + + ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(-100, -100), textcoords='offset points', + size=20, + #bbox=dict(boxstyle="round", fc="0.8"), + arrowprops=dict(arrowstyle="wedge,tail_width=0.7", + fc="0.6", ec="none", + patchB=el, + connectionstyle="arc3,rad=-0.3"), + ) + + + ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(0, -45), textcoords='offset points', + size=20, + bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec=(1., .5, .5)), + arrowprops=dict(arrowstyle="wedge,tail_width=1.", + fc=(1.0, 0.7, 0.7), ec=(1., .5, .5), + patchA=None, + patchB=el, + relpos=(0.2, 0.8), + connectionstyle="arc3,rad=-0.1"), + ) + + ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', + xytext=(35, 0), textcoords='offset points', + size=20, va="center", + bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec="none"), + arrowprops=dict(arrowstyle="wedge,tail_width=1.", + fc=(1.0, 0.7, 0.7), ec="none", + patchA=None, + patchB=el, + relpos=(0.2, 0.5), + ) + ) + + fig.savefig('annotation_arrowstyle') + +show() Added: trunk/matplotlib/lib/matplotlib/bezier.py =================================================================== --- trunk/matplotlib/lib/matplotlib/bezier.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/bezier.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -0,0 +1,478 @@ +""" +A module providing some utility functions regarding bezier path manipulation. +""" + + +import numpy as np +from math import sqrt + +from matplotlib.path import Path + +from operator import xor + + +# some functions + +def get_intersection(cx1, cy1, cos_t1, sin_t1, + cx2, cy2, cos_t2, sin_t2): + """ return a intersecting point between a line through (cx1, cy1) + and having angle t1 and a line through (cx2, cy2) and angle t2. + """ + + # line1 => sin_t1 * (x - cx1) - cos_t1 * (y - cy1) = 0. + # line1 => sin_t1 * x + cos_t1 * y = sin_t1*cx1 - cos_t1*cy1 + + line1_rhs = sin_t1 * cx1 - cos_t1 * cy1 + line2_rhs = sin_t2 * cx2 - cos_t2 * cy2 + + # rhs matrix + a, b = sin_t1, -cos_t1 + c, d = sin_t2, -cos_t2 + + ad_bc = a*d-b*c + if ad_bc == 0.: + raise ValueError("Given lines do not intersect") + + #rhs_inverse + a_, b_ = d, -b + c_, d_ = -c, a + a_, b_, c_, d_ = [k / ad_bc for k in [a_, b_, c_, d_]] + + x = a_* line1_rhs + b_ * line2_rhs + y = c_* line1_rhs + d_ * line2_rhs + + return x, y + + + +def get_normal_points(cx, cy, cos_t, sin_t, length): + """ + For a line passing through (*cx*, *cy*) and having a angle *t*, + return locations of the two points located along its perpendicular line at the distance of *length*. + """ + + if length == 0.: + return cx, cy, cx, cy + + cos_t1, sin_t1 = sin_t, -cos_t + cos_t2, sin_t2 = -sin_t, cos_t + + x1, y1 = length*cos_t1 + cx, length*sin_t1 + cy + x2, y2 = length*cos_t2 + cx, length*sin_t2 + cy + + return x1, y1, x2, y2 + + + + +## BEZIER routines + + + + + +# subdividing bezier curve +# http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-sub.html + +def _de_casteljau1(beta, t): + next_beta = beta[:-1] * (1-t) + beta[1:] * t + return next_beta + +def split_de_casteljau(beta, t): + """split a bezier segment defined by its controlpoints *beta* + into two separate segment divided at *t* and return their control points. + + """ + beta = np.asarray(beta) + beta_list = [beta] + while True: + beta = _de_casteljau1(beta, t) + beta_list.append(beta) + if len(beta) == 1: + break + left_beta = [beta[0] for beta in beta_list] + right_beta = [beta[-1] for beta in reversed(beta_list)] + + return left_beta, right_beta + + + + + + + +def find_bezier_t_intersecting_with_closedpath(bezier_point_at_t, inside_closedpath, + t0=0., t1=1., tolerence=0.01): + """ Find a parameter t0 and t1 of the given bezier path which + bounds the intersecting points with a provided closed + path(*inside_closedpath*). Search starts from *t0* and *t1* and it + uses a simple bisecting algorithm therefore one of the end point + must be inside the path while the orther doesn't. The search stop + when |t0-t1| gets smaller than the given tolerence. + value for + + - bezier_point_at_t : a function which returns x, y coordinates at *t* + + - inside_closedpath : return True if the point is insed the path + + """ + # inside_closedpath : function + + start = bezier_point_at_t(t0) + end = bezier_point_at_t(t1) + + start_inside = inside_closedpath(start) + end_inside = inside_closedpath(end) + + if not xor(start_inside, end_inside): + raise ValueError("the segment does not seemed to intersect with the path") + + while 1: + + # return if the distance is smaller than the tolerence + if (start[0]-end[0])**2 + (start[1]-end[1])**2 < tolerence**2: + return t0, t1 + + # calculate the middle point + middle_t = 0.5*(t0+t1) + middle = bezier_point_at_t(middle_t) + middle_inside = inside_closedpath(middle) + + if xor(start_inside, middle_inside): + t1 = middle_t + end = middle + end_inside = middle_inside + else: + t0 = middle_t + start = middle + start_inside = middle_inside + + + + + +class BezierSegment: + """ + A simple class of a 2-dimensional bezier segment + """ + + # Highrt order bezier lines can be supported by simplying adding + # correcponding values. + _binom_coeff = {1:np.array([1., 1.]), + 2:np.array([1., 2., 1.]), + 3:np.array([1., 3., 3., 1.])} + + def __init__(self, control_points): + """ + *control_points* : location of contol points. It needs have a + shpae of n * 2, where n is the order of the bezier line. 1<= + n <= 3 is supported. + """ + _o = len(control_points) + self._orders = np.arange(_o) + _coeff = BezierSegment._binom_coeff[_o - 1] + + _control_points = np.asarray(control_points) + xx = _control_points[:,0] + yy = _control_points[:,1] + + self._px = xx * _coeff + self._py = yy * _coeff + + def point_at_t(self, t): + "evaluate a point at t" + one_minus_t_powers = np.power(1.-t, self._orders)[::-1] + t_powers = np.power(t, self._orders) + + tt = one_minus_t_powers * t_powers + _x = sum(tt * self._px) + _y = sum(tt * self._py) + + return _x, _y + + +def split_bezier_intersecting_with_closedpath(bezier, + inside_closedpath, + tolerence=0.01): + + """ + bezier : control points of the bezier segment + inside_closedpath : a function which returns true if the point is inside the path + """ + + bz = BezierSegment(bezier) + bezier_point_at_t = bz.point_at_t + + t0, t1 = find_bezier_t_intersecting_with_closedpath(bezier_point_at_t, + inside_closedpath, + tolerence=tolerence) + + _left, _right = split_de_casteljau(bezier, (t0+t1)/2.) + return _left, _right + + + +def find_r_to_boundary_of_closedpath(inside_closedpath, xy, + cos_t, sin_t, + rmin=0., rmax=1., tolerence=0.01): + """ + Find a radius r (centered at *xy*) between *rmin* and *rmax* at + which it intersect with the path. + + inside_closedpath : function + cx, cy : center + cos_t, sin_t : cosine and sine for the angle + rmin, rmax : + """ + + cx, cy = xy + def _f(r): + return cos_t*r + cx, sin_t*r + cy + + find_bezier_t_intersecting_with_closedpath(_f, inside_closedpath, + t0=rmin, t1=rmax, tolerence=tolerence) + + + +## matplotlib specific + +def split_path_inout(path, inside, tolerence=0.01, reorder_inout=False): + """ divide a path into two segment at the point where inside(x, y) + becomes False. + """ + + path_iter = path.iter_segments() + + ctl_points, command = path_iter.next() + begin_inside = inside(ctl_points[-2:]) # true if begin point is inside + + bezier_path = None + ctl_points_old = ctl_points + + concat = np.concatenate + + iold=0 + i = 1 + + for ctl_points, command in path_iter: + iold=i + i += len(ctl_points)/2 + if inside(ctl_points[-2:]) != begin_inside: + bezier_path = concat([ctl_points_old[-2:], ctl_points]) + break + + ctl_points_old = ctl_points + + if bezier_path is None: + raise ValueError("The path does not seem to intersect with the patch") + + bp = zip(bezier_path[::2], bezier_path[1::2]) + left, right = split_bezier_intersecting_with_closedpath(bp, + inside, + tolerence) + if len(left) == 2: + codes_left = [Path.LINETO] + codes_right = [Path.MOVETO, Path.LINETO] + elif len(left) == 3: + codes_left = [Path.CURVE3, Path.CURVE3] + codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3] + elif len(left) == 4: + codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4] + codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4] + else: + raise ValueError() + + verts_left = left[1:] + verts_right = right[:] + + #i += 1 + + if path.codes is None: + path_in = Path(concat([path.vertices[:i], verts_left])) + path_out = Path(concat([verts_right, path.vertices[i:]])) + + else: + path_in = Path(concat([path.vertices[:iold], verts_left]), + concat([path.codes[:iold], codes_left])) + + path_out = Path(concat([verts_right, path.vertices[i:]]), + concat([codes_right, path.codes[i:]])) + + if reorder_inout and begin_inside == False: + path_in, path_out = path_out, path_in + + return path_in, path_out + + + + + +def inside_circle(cx, cy, r): + r2 = r**2 + def _f(xy): + x, y = xy + return (x-cx)**2 + (y-cy)**2 < r2 + return _f + + + +# quadratic bezier lines + +def get_cos_sin(x0, y0, x1, y1): + dx, dy = x1-x0, y1-y0 + d = (dx*dx + dy*dy)**.5 + return dx/d, dy/d + + +def get_parallels(bezier2, width): + """ + Given the quadraitc bezier control points *bezier2*, returns + control points of quadrativ bezier lines roughly parralel to given + one separated by *width*. + """ + + # The parallel bezier lines constructed by following ways. + # c1 and c2 are contol points representing the begin and end of the bezier line. + # cm is the middle point + c1x, c1y = bezier2[0] + cmx, cmy = bezier2[1] + c2x, c2y = bezier2[2] + + # t1 and t2 is the anlge between c1 and cm, cm, c2. + # They are also a angle of the tangential line of the path at c1 and c2 + cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) + cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c2x, c2y) + + # find c1_left, c1_right which are located along the lines + # throught c1 and perpendicular to the tangential lines of the + # bezier path at a distance of width. Same thing for c2_left and + # c2_right with respect to c2. + c1x_left, c1y_left, c1x_right, c1y_right = \ + get_normal_points(c1x, c1y, cos_t1, sin_t1, width) + c2x_left, c2y_left, c2x_right, c2y_right = \ + get_normal_points(c2x, c2y, cos_t2, sin_t2, width) + + # find cm_left which is the intersectng point of a line through + # c1_left with angle t1 and a line throught c2_left with angle + # t2. Same with cm_right. + cmx_left, cmy_left = get_intersection(c1x_left, c1y_left, cos_t1, sin_t1, + c2x_left, c2y_left, cos_t2, sin_t2) + + cmx_right, cmy_right = get_intersection(c1x_right, c1y_right, cos_t1, sin_t1, + c2x_right, c2y_right, cos_t2, sin_t2) + + # the parralel bezier lines are created with control points of + # [c1_left, cm_left, c2_left] and [c1_right, cm_right, c2_right] + path_left = [(c1x_left, c1y_left), (cmx_left, cmy_left), (c2x_left, c2y_left)] + path_right = [(c1x_right, c1y_right), (cmx_right, cmy_right), (c2x_right, c2y_right)] + + return path_left, path_right + + + +def make_wedged_bezier2(bezier2, length, shrink_factor=0.5): + """ + Being similar to get_parallels, returns + control points of two quadrativ bezier lines having a width roughly parralel to given + one separated by *width*. + """ + + xx1, yy1 = bezier2[2] + xx2, yy2 = bezier2[1] + xx3, yy3 = bezier2[0] + + cx, cy = xx3, yy3 + x0, y0 = xx2, yy2 + + dist = sqrt((x0-cx)**2 + (y0-cy)**2) + cos_t, sin_t = (x0-cx)/dist, (y0-cy)/dist, + + x1, y1, x2, y2 = get_normal_points(cx, cy, cos_t, sin_t, length) + + xx12, yy12 = (xx1+xx2)/2., (yy1+yy2)/2., + xx23, yy23 = (xx2+xx3)/2., (yy2+yy3)/2., + + dist = sqrt((xx12-xx23)**2 + (yy12-yy23)**2) + cos_t, sin_t = (xx12-xx23)/dist, (yy12-yy23)/dist, + + xm1, ym1, xm2, ym2 = get_normal_points(xx2, yy2, cos_t, sin_t, length*shrink_factor) + + l_plus = [(x1, y1), (xm1, ym1), (xx1, yy1)] + l_minus = [(x2, y2), (xm2, ym2), (xx1, yy1)] + + return l_plus, l_minus + + +def find_control_points(c1x, c1y, mmx, mmy, c2x, c2y): + """ Find control points of the bezier line throught c1, mm, c2. We + simply assume that c1, mm, c2 which have parameteric value 0, 0.5, and 1. + """ + + cmx = .5 * (4*mmx - (c1x + c2x)) + cmy = .5 * (4*mmy - (c1y + c2y)) + + return [(c1x, c1y), (cmx, cmy), (c2x, c2y)] + + +def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.): + """ + Being similar to get_parallels, returns + control points of two quadrativ bezier lines having a width roughly parralel to given + one separated by *width*. + """ + + # c1, cm, c2 + c1x, c1y = bezier2[0] + cmx, cmy = bezier2[1] + c3x, c3y = bezier2[2] + + + # t1 and t2 is the anlge between c1 and cm, cm, c3. + # They are also a angle of the tangential line of the path at c1 and c3 + cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) + cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c3x, c3y) + + # find c1_left, c1_right which are located along the lines + # throught c1 and perpendicular to the tangential lines of the + # bezier path at a distance of width. Same thing for c3_left and + # c3_right with respect to c3. + c1x_left, c1y_left, c1x_right, c1y_right = \ + get_normal_points(c1x, c1y, cos_t1, sin_t1, width*w1) + c3x_left, c3y_left, c3x_right, c3y_right = \ + get_normal_points(c3x, c3y, cos_t2, sin_t2, width*w2) + + + + + # find c12, c23 and c123 which are middle points of c1-cm, cm-c3 and c12-c23 + c12x, c12y = (c1x+cmx)*.5, (c1y+cmy)*.5 + c23x, c23y = (cmx+c3x)*.5, (cmy+c3y)*.5 + c123x, c123y = (c12x+c23x)*.5, (c12y+c23y)*.5 + + # tangential angle of c123 (angle between c12 and c23) + cos_t123, sin_t123 = get_cos_sin(c12x, c12y, c23x, c23y) + + c123x_left, c123y_left, c123x_right, c123y_right = \ + get_normal_points(c123x, c123y, cos_t123, sin_t123, width*wm) + + + path_left = find_control_points(c1x_left, c1y_left, + c123x_left, c123y_left, + c3x_left, c3y_left) + path_right = find_control_points(c1x_right, c1y_right, + c123x_right, c123y_right, + c3x_right, c3y_right) + + return path_left, path_right + + + + +if 0: + path = Path([(0, 0), (1, 0), (2, 2)], + [Path.MOVETO, Path.CURVE3, Path.CURVE3]) + left, right = divide_path_inout(path, inside) + clf() + ax = gca() + + Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-29 20:28:57 UTC (rev 6352) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 13:18:07 UTC (rev 6353) @@ -1475,7 +1475,153 @@ return path +class Round4BoxTransmuter(BboxTransmuterBase): + """ + A box with round edges. + """ + def __init__(self, pad=0.3, rounding_size=None): + self.pad = pad + self.rounding_size = rounding_size + BboxTransmuterBase.__init__(self) + + def transmute(self, x0, y0, width, height, mutation_size): + + # padding + pad = mutation_size * self.pad + + # roudning size. Use a half of the pad if not set. + if self.rounding_size: + dr = mutation_size * self.rounding_size + else: + dr = pad / 2. + + width, height = width + 2.*pad - 2*dr, \ + height + 2.*pad - 2*dr, + + + x0, y0 = x0-pad+dr, y0-pad+dr, + x1, y1 = x0+width, y0 + height + + + cp = [(x0, y0), + (x0+dr, y0-dr), (x1-dr, y0-dr), (x1, y0), + (x1+dr, y0+dr), (x1+dr, y1-dr), (x1, y1), + (x1-dr, y1+dr), (x0+dr, y1+dr), (x0, y1), + (x0-dr, y1-dr), (x0-dr, y0+dr), (x0, y0), + (x0, y0)] + + com = [Path.MOVETO, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CLOSEPOLY] + + path = Path(cp, com) + + return path + + + + +class SawtoothBoxTransmuter(BboxTransmuterBase): + """ + A sawtooth box. + """ + + def __init__(self, pad=0.3, tooth_size=None): + self.pad = pad + self.tooth_size = tooth_size + BboxTransmuterBase.__init__(self) + + def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): + + + # padding + pad = mutation_size * self.pad + + # size of sawtooth + if self.tooth_size is None: + tooth_size = self.pad * .5 * mutation_size + else: + tooth_size = self.tooth_size * mutation_size + + tooth_size2 = tooth_size / 2. + width, height = width + 2.*pad - tooth_size, \ + height + 2.*pad - tooth_size, + + # the sizes of the vertical and horizontal sawtooth are + # separately adjusted to fit the given box size. + dsx_n = int(round((width - tooth_size) / (tooth_size * 2))) * 2 + dsx = (width - tooth_size) / dsx_n + dsy_n = int(round((height - tooth_size) / (tooth_size * 2))) * 2 + dsy = (height - tooth_size) / dsy_n + + + x0, y0 = x0-pad+tooth_size2, y0-pad+tooth_size2 + x1, y1 = x0+width, y0 + height + + + bottom_saw_x = [x0] + \ + [x0 + tooth_size2 + dsx*.5* i for i in range(dsx_n*2)] + \ + [x1 - tooth_size2] + bottom_saw_y = [y0] + \ + [y0 - tooth_size2, y0, y0 + tooth_size2, y0] * dsx_n + \ + [y0 - tooth_size2] + + right_saw_x = [x1] + \ + [x1 + tooth_size2, x1, x1 - tooth_size2, x1] * dsx_n + \ + [x1 + tooth_size2] + right_saw_y = [y0] + \ + [y0 + tooth_size2 + dsy*.5* i for i in range(dsy_n*2)] + \ + [y1 - tooth_size2] + + top_saw_x = [x1] + \ + [x1 - tooth_size2 - dsx*.5* i for i in range(dsx_n*2)] + \ + [x0 + tooth_size2] + top_saw_y = [y1] + \ + [y1 + tooth_size2, y1, y1 - tooth_size2, y1] * dsx_n + \ + [y1 + tooth_size2] + + left_saw_x = [x0] + \ + [x0 - tooth_size2, x0, x0 + tooth_size2, x0] * dsy_n + \ + [x0 - tooth_size2] + left_saw_y = [y1] + \ + [y1 - tooth_size2 - dsy*.5* i for i in range(dsy_n*2)] + \ + [y0 + tooth_size2] + + saw_vertices = zip(bottom_saw_x, bottom_saw_y) + \ + zip(right_saw_x, right_saw_y) + \ + zip(top_saw_x, top_saw_y) + \ + zip(left_saw_x, left_saw_y) + \ + [(bottom_saw_x[0], bottom_saw_y[0])] + + return saw_vertices + + + def transmute(self, x0, y0, width, height, mutation_size): + + saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) + path = Path(saw_vertices) + return path + + +class RoundtoothBoxTransmuter(SawtoothBoxTransmuter): + """ + A roundtooth(?) box. + """ + + def transmute(self, x0, y0, width, height, mutation_size): + + saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) + + cp = [Path.MOVETO] + ([Path.CURVE3, Path.CURVE3] * ((len(saw_vertices)-1)//2)) + path = Path(saw_vertices, cp) + + return path + + def _list_available_boxstyles(transmuters): """ a helper function of the :class:`FancyBboxPatch` to list the available box styles. It inspects the arguments of the __init__ methods of @@ -1520,6 +1666,9 @@ _fancy_bbox_transmuters = {"square":SquareBoxTransmuter, "round":RoundBoxTransmuter, + "round4":Round4BoxTransmuter, + "sawtooth":SawtoothBoxTransmuter, + "roundtooth":RoundtoothBoxTransmuter, } def __str__(self): @@ -1585,6 +1734,7 @@ __init__.__doc__ = cbook.dedent(__init__.__doc__) % kwdoc del kwdoc + @classmethod def list_available_boxstyles(cls): return _list_available_boxstyles(cls._fancy_bbox_transmuters) @@ -1758,3 +1908,1123 @@ def get_bbox(self): return transforms.Bbox.from_bounds(self._x, self._y, self._width, self._height) + + + + +from matplotlib.bezier import split_bezier_intersecting_with_closedpath +from matplotlib.bezier import get_intersection, inside_circle, get_parallels +from matplotlib.bezier import make_wedged_bezier2 +from matplotlib.bezier import split_path_inout, inside_circle + +class ConnectorBase(object): + """ The ConnectorClass is used to define a path between a two + points. This class is used in the FancyArrowPatch class. It + creates a path between point A and point B. When optional patch + objects (pathcA & patchB) are provided and if they enclose the + point A or B, the path is clipped to the boundary of the each + patch. Additionally the path can be shirnked by a fixed size + (given in points) with shrinkA and shrinkB. + """ + + class SimpleEvent: + def __init__(self, xy): + self.x, self.y = xy + + def _clip(self, path, patchA, patchB): + """ Clip the path to the boundary of the patchA and patchB. + The starting point of the path needed to be inside of the + patchA and the end point inside the patch B. The contains + methods of each patch object is utilized to test if the point + is inside the path. + """ + + if patchA: + def insideA(xy_display): + #xy_display = patchA.get_data_transform().transform_point(xy_data) + xy_event = ConnectorBase.SimpleEvent(xy_display) + return patchA.contains(xy_event)[0] + + try: + left, right = split_path_inout(path, insideA) + except ValueError: + right = path + + path = right + + if patchB: + def insideB(xy_display): + #xy_display = patchB.get_data_transform().transform_point(xy_data) + xy_event = ConnectorBase.SimpleEvent(xy_display) + return patchB.contains(xy_event)[0] + + try: + left, right = split_path_inout(path, insideB) + except ValueError: + left = path + + path = left + + #ppp = patchB.get_patch_transform().transform_path(patchB.get_path()) + #def insideB(xy_data): + # return ppp.contains_point(xy_data) + ##return patchB.contains(ConnectorBase.SimpleEvent(xy))[0] + + return path + + + def _shrink(self, path, shrinkA, shrinkB): + """ + Shrink the path by fixed size (in points) with shrinkA and shrinkB + """ + if shrinkA: + x, y = path.vertices[0] + insideA = inside_circle(x, y, shrinkA) + + left, right = split_path_inout(path, insideA) + path = right + + if shrinkB: + x, y = path.vertices[-1] + insideB = inside_circle(x, y, shrinkB) + + left, right = split_path_inout(path, insideB) + path = left + + return path + + def __call__(self, posA, posB, + shrinkA=2., shrinkB=2., patchA=None, patchB=None): + + path = self.connect(posA, posB) + + clipped_path = self._clip(path, patchA, patchB) + shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) + + return shrinked_path + + +class Arc3Connector(ConnectorBase): + """ Creates a simple quadratic bezier curve between two + points. The curve is created so that the middle contol points (C1) + is located at the same distance from the start (C0) and end + points(C2) and the distance of the C1 to the line connecting C0-C2 + is *rad* times the distance of C0-C2. + """ + def __init__(self, rad=0.): + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + x12, y12 = (x1 + x2)/2., (y1 + y2)/2. + dx, dy = x2 - x1, y2 - y1 + + f = self.rad + + cx, cy = x12 + f*dy, y12 - f*dx + + vertices = [(x1, y1), + (cx, cy), + (x2, y2)] + codes = [Path.MOVETO, + Path.CURVE3, + Path.CURVE3] + + return Path(vertices, codes) + + +class Angle3Connector(ConnectorBase): + """ Creates a simple quadratic bezier curve between two + points. The middle control points is placed at the intersecting + point of two lines which crosses the start (or end) point + and has a angle of angleA (or angleB). + """ + def __init__(self, angleA=90, angleB=0): + self.angleA = angleA + self.angleB = angleB + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + cosA, sinA = math.cos(self.angleA/180.*math.pi),\ + math.sin(self.angleA/180.*math.pi), + cosB, sinB = math.cos(self.angleB/180.*math.pi),\ + math.sin(self.angleB/180.*math.pi), + + cx, cy = get_intersection(x1, y1, cosA, sinA, + x2, y2, cosB, sinB) + + vertices = [(x1, y1), (cx, cy), (x2, y2)] + codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] + + return Path(vertices, codes) + + +class AngleConnector(ConnectorBase): + """ Creates a picewise continuous quadratic bezier path between + two points. The path has a one passing-through point placed at the + intersecting point of two lines which crosses the start (or end) + point and has a angle of angleA (or angleB). The connecting edges are + rounded with *rad*. + """ + + def __init__(self, angleA=90, angleB=0, rad=0.): + self.angleA = angleA + self.angleB = angleB + + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + cosA, sinA = math.cos(self.angleA/180.*math.pi),\ + math.sin(self.angleA/180.*math.pi), + cosB, sinB = math.cos(self.angleB/180.*math.pi),\ + -math.sin(self.angleB/180.*math.pi), + + cx, cy = get_intersection(x1, y1, cosA, sinA, + x2, y2, cosB, sinB) + + vertices = [(x1, y1)] + codes = [Path.MOVETO] + + if self.rad == 0.: + vertices.append((cx, cy)) + codes.append(Path.LINETO) + else: + vertices.extend([(cx - self.rad * cosA, cy - self.rad * sinA), + (cx, cy), + (cx + self.rad * cosB, cy + self.rad * sinB)]) + codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3]) + + vertices.append((x2, y2)) + codes.append(Path.LINETO) + + return Path(vertices, codes) + + + +class ArcConnector(ConnectorBase): + """ Creates a picewise continuous quadratic bezier path between + two points. The path can have two passing-through points, a point + placed at the distance of armA and angle of angleA from point A, + another point with respect to point B. The edges are rounded with + *rad*. + """ + + def __init__(self, angleA=0, angleB=0, armA=None, armB=None, rad=0.): + self.angleA = angleA + self.angleB = angleB + self.armA = armA + self.armB = armB + + self.rad = rad + + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB + + vertices = [(x1, y1)] + rounded = [] + codes = [Path.MOVETO] + + if self.armA: + cosA = math.cos(self.angleA/180.*math.pi) + sinA = math.sin(self.angleA/180.*math.pi) + #x_armA, y_armB + d = self.armA - self.rad + rounded.append((x1 + d*cosA, y1 + d*sinA)) + d = self.armA + rounded.append((x1 + d*cosA, y1 + d*sinA)) + + if self.armB: + cosB = math.cos(self.angleB/180.*math.pi) + sinB = math.sin(self.angleB/180.*math.pi) + x_armB, y_armB = x2 + self.armB*cosB, y2 + self.armB*sinB + + if rounded: + xp, yp = rounded[-1] + dx, dy = x_armB - xp, y_armB - yp + dd = (dx*dx + dy*dy)**.5 + + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) + vertices.extend(rounded) + codes.extend([Path.LINETO, + Path.CURVE3, + Path.CURVE3]) + else: + xp, yp = vertices[-1] + dx, dy = x_armB - xp, y_armB - yp + dd = (dx*dx + dy*dy)**.5 + + d = dd - self.rad + rounded = [(xp + d*dx/dd, yp + d*dy/dd), + (x_armB, y_armB)] + + if rounded: + xp, yp = rounded[-1] + dx, dy = x2 - xp, y2 - yp + dd = (dx*dx + dy*dy)**.5 + + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) + vertices.extend(rounded) + codes.extend([Path.LINETO, + Path.CURVE3, + Path.CURVE3]) + + vertices.append((x2, y2)) + codes.append(Path.LINETO) + + return Path(vertices, codes) + + + + +class ArrowTransmuterBase(object): + """ + Arrow Transmuter Base class + + ArrowTransmuterBase and its derivatives are used to make a fancy + arrow around a given path. The __call__ method returns a path + (which will be used to create a PathPatch instance) and a boolean + value indicating the path is open therefore is not fillable. This + class is not an artist and actual drawing of the fancy arrow is + done by the FancyArrowPatch class. + + """ + + # The derived classes are required to be able to be initialized + # w/o arguments, i.e., all its argument (except self) must have + # the default values. + + def __init__(self): + super(ArrowTransmuterBase, self).__init__() + + @staticmethod + def ensure_quadratic_bezier(path): + """ Some ArrowTransmuter class only wokrs with a simple + quaratic bezier curve (created with Arc3Connetion or + Angle3Connector). This static method is to check if the + provided path is a simple quadratic bezier curve and returns + its control points if true. + """ + segments = list(path.iter_segments()) + assert len(segments) == 2 + + assert segments[0][1] == Path.MOVETO + assert segments[1][1] == Path.CURVE3 + + return list(segments[0][0]) + list(segments[1][0]) + + + def transmute(self, path, mutation_size, linewidth): + """ + The transmute method is a very core of the ArrowTransmuter + class and must be overriden in the subclasses. It receives the + path object along which the arrow will be drawn, and the + mutation_size, with which the amount arrow head and etc. will + be scaled. It returns a Path instance. The linewidth may be + used to adjust the the path so that it does not pass beyond + the given points. + """ + + raise NotImplementedError('Derived must override') + + + + def __call__(self, path, mutation_size, linewidth, + aspect_ratio=1.): + """ + The __call__ method is a thin wrapper around the transmute method + and take care of the aspect ratio. + """ + + if aspect_ratio is not None: + # Squeeze the given height by the aspect_ratio + + vertices, codes = path.vertices[:], path.codes[:] + # Squeeze the height + vertices[:,1] = vertices[:,1] / aspect_ratio + path_shrinked = Path(vertices, codes) + # call transmute method with squeezed height. + path_mutated, closed = self.transmute(path_shrinked, linewidth, + mutation_size) + vertices, codes = path_mutate.vertices, path_mutate.codes + # Restore the height + vertices[:,1] = vertices[:,1] * aspect_ratio + return Path(vertices, codes), closed + else: + return self.transmute(path, mutation_size, linewidth) + + + +class CurveArrowTransmuter(ArrowTransmuterBase): + """ + A simple arrow which will work with any path instance. The + returned path is simply concatenation of the original path + at + most two paths representing the arrow at the begin point and the + at the end point. The returned path is not closed and only meant + to be stroked. + """ + + def __init__(self, beginarrow=None, endarrow=None, + head_length=.2, head_width=.1): + """ The arrows are drawn if *beginarrow* and/or *endarrow* are + true. *head_length* and *head_width* determines the size of + the arrow relative to the *mutation scale*. + """ + self.beginarrow, self.endarrow = beginarrow, endarrow + self.head_length, self.head_width = \ + head_length, head_width + super(CurveArrowTransmuter, self).__init__() + + + def _get_pad_projected(self, x0, y0, x1, y1, linewidth): + # when no arrow head is drawn + + dx, dy = x0 - x1, y0 - y1 + cp_distance = math.sqrt(dx**2 + dy**2) + + # padx_projected, pady_projected : amount of pad to account + # projection of the wedge + padx_projected = (.5*linewidth) + pady_projected = (.5*linewidth) + + # apply pad for projected edge + ddx = padx_projected * dx / cp_distance + ddy = pady_projected * dy / cp_distance + + return ddx, ddy + + def _get_arrow_wedge(self, x0, y0, x1, y1, + head_dist, cos_t, sin_t, linewidth + ): + """ Return the paths for arrow heads. Since arrow lines are + drawn with capstyle=projected, The arrow is goes beyond the + desired point. This method also returns the amount of the path + to be shrinked so that it does not overshoot. + """ + + # arrow from x0, y0 to x1, y1 + + + dx, dy = x0 - x1, y0 - y1 + cp_distance = math.sqrt(dx**2 + dy**2) + + # padx_projected, pady_projected : amount of pad for account + # the overshooting of the projection of the wedge + padx_projected = (.5*linewidth / cos_t) + pady_projected = (.5*linewidth / sin_t) + + # apply pad for projected edge + ddx = padx_projected * dx / cp_distance + ddy = pady_projected * dy / cp_distance + + # offset for arrow wedge + dx, dy = dx / cp_distance * head_dist, dy / cp_distance * head_dist + + dx1, dy1 = cos_t * dx + sin_t * dy, -sin_t * dx + cos_t * dy + dx2, dy2 = cos_t * dx - sin_t * dy, sin_t * dx + cos_t * dy + + vertices_arrow = [(x1+ddx+dx1, y1+ddy+dy1), + (x1+ddx, y1++ddy), + (x1+ddx+dx2, y1+ddy+dy2)] + codes_arrow = [Path.MOVETO, + Path.LINETO, + Path.LINETO] + + return vertices_arrow, codes_arrow, ddx, ddy + + + def transmute(self, path, mutation_size, linewidth): + + head_length, head_width = self.head_length * mutation_size, \ + self.head_width * mutation_size + head_dist = math.sqrt(head_length**2 + head_width**2) + cos_t, sin_t = head_length / head_dist, head_width / head_dist + + + # begin arrow + x0, y0 = path.vertices[0] + x1, y1 = path.vertices[1] + + if self.beginarrow: + verticesA, codesA, ddxA, ddyA = \ + self._get_arrow_wedge(x1, y1, x0, y0, + head_dist, cos_t, sin_t, + linewidth) + else: + verticesA, codesA = [], [] + #ddxA, ddyA = self._get_pad_projected(x1, y1, x0, y0, linewidth) + ddxA, ddyA = 0., 0., #self._get_pad_projected(x1, y1, x0, y0, linewidth) + + # end arrow + x2, y2 = path.vertices[-2] + x3, y3 = path.vertices[-1] + + if self.endarrow: + verticesB, codesB, ddxB, ddyB = \ + self._get_arrow_wedge(x2, y2, x3, y3, + head_dist, cos_t, sin_t, + linewidth) + else: + verticesB, codesB = [], [] + ddxB, ddyB = 0., 0. #self._get_pad_projected(x2, y2, x3, y3, linewidth) + + + # this simple code will not work if ddx, ddy is greater than + # separation bettern vertices. + vertices = np.concatenate([verticesA + [(x0+ddxA, y0+ddyA)], + path.vertices[1:-1], + [(x3+ddxB, y3+ddyB)] + verticesB]) + codes = np.concatenate([codesA, + path.codes, + codesB]) + + p = Path(vertices, codes) + + return p, False + + +class CurveArrowATransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrow at begin point. This class is + only meant to be used to define the arrowstyle and users may + simply use the original CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowATransmuter, self).__init__( \ + beginarrow=True, endarrow=False, + head_length=head_length, head_width=head_width ) + + +class CurveArrowBTransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrow at end point. This class is + only meant to be used to define the arrowstyle and users may + simply use the original CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowBTransmuter, self).__init__( \ + beginarrow=False, endarrow=True, + head_length=head_length, head_width=head_width ) + + +class CurveArrowABTransmuter(CurveArrowTransmuter): + """ + A CurveArrowTransmuter with arrows at both begin and end + points. This class is only meant to be used to define the + arrowstyle and users may simply use the original + CurveArrowTransmuter class when necesary. + """ + + def __init__(self, head_length=.4, head_width=.2): + super(CurveArrowABTransmuter, self).__init__( \ + beginarrow=True, endarrow=True, + head_length=head_length, head_width=head_width ) + + + +class SimpleArrowTransmuter(ArrowTransmuterBase): + """ + A simple arrow. Only works with a quadratic bezier curve. + """ + + def __init__(self, head_length=.5, head_width=.5, tail_width=.2): + self.head_length, self.head_width, self.tail_width = \ + head_length, head_width, tail_width + super(SimpleArrowTransmuter, self).__init__() + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + # divide the path into a head and a tail + head_length = self.head_length * mutation_size + in_f = inside_circle(x2, y2, head_length) + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + arrow_out, arrow_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + + # head + head_width = self.head_width * mutation_size + head_l, head_r = make_wedged_bezier2(arrow_in, head_width/2., + wm=.8) + + + + # tail + tail_width = self.tail_width * mutation_size + tail_left, tail_right = get_parallels(arrow_out, tail_width/2.) + + head_right, head_left = head_r, head_l + patch_path = [(Path.MOVETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.LINETO, tail_right[0]), + (Path.CLOSEPOLY, tail_right[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + +class FancyArrowTransmuter(ArrowTransmuterBase): + """ + A fancy arrow. Only works with a quadratic bezier curve. + """ + + def __init__(self, head_length=.4, head_width=.4, tail_width=.4): + self.head_length, self.head_width, self.tail_width = \ + head_length, head_width, tail_width + super(FancyArrowTransmuter, self).__init__() + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + # divide the path into a head and a tail + head_length = self.head_length * mutation_size + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + + # path for head + in_f = inside_circle(x2, y2, head_length) + path_out, path_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + path_head = path_in + + # path for head + in_f = inside_circle(x2, y2, head_length*.8) + path_out, path_in = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + path_tail = path_out + + + # head + head_width = self.head_width * mutation_size + head_l, head_r = make_wedged_bezier2(path_head, head_width/2., + wm=.6) + + # tail + tail_width = self.tail_width * mutation_size + tail_left, tail_right = make_wedged_bezier2(path_tail, + tail_width*.5, + w1=1., wm=0.6, w2=0.3) + + # path for head + in_f = inside_circle(x0, y0, tail_width*.3) + path_in, path_out = \ + split_bezier_intersecting_with_closedpath(arrow_path, + in_f, + tolerence=0.01) + tail_start = path_in[-1] + + head_right, head_left = head_r, head_l + patch_path = [(Path.MOVETO, tail_start), + (Path.LINETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.LINETO, tail_start), + (Path.CLOSEPOLY, tail_start), + ] + patch_path2 = [(Path.MOVETO, tail_right[0]), + (Path.CURVE3, tail_right[1]), + (Path.CURVE3, tail_right[2]), + (Path.LINETO, head_right[0]), + (Path.CURVE3, head_right[1]), + (Path.CURVE3, head_right[2]), + (Path.CURVE3, head_left[1]), + (Path.CURVE3, head_left[0]), + (Path.LINETO, tail_left[2]), + (Path.CURVE3, tail_left[1]), + (Path.CURVE3, tail_left[0]), + (Path.CURVE3, tail_start), + (Path.CURVE3, tail_right[0]), + (Path.CLOSEPOLY, tail_right[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + + + + +class WedgeArrowTransmuter(ArrowTransmuterBase): + """ + Wedge(?) shape. Only wokrs with a quadratic bezier curve. The + begin point has a width of the tail_width and the end point has a + width of 0. At the middle, the width is shrink_factor*tail_width. + """ + + def __init__(self, tail_width=.3, shrink_factor=0.5): + self.tail_width = tail_width + self.shrink_factor = shrink_factor + super(WedgeArrowTransmuter, self).__init__() + + + def transmute(self, path, mutation_size, linewidth): + + x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) + + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] + b_plus, b_minus = make_wedged_bezier2(arrow_path, + self.tail_width * mutation_size / 2., + wm=self.shrink_factor) + + + patch_path = [(Path.MOVETO, b_plus[0]), + (Path.CURVE3, b_plus[1]), + (Path.CURVE3, b_plus[2]), + (Path.LINETO, b_minus[2]), + (Path.CURVE3, b_minus[1]), + (Path.CURVE3, b_minus[0]), + (Path.CLOSEPOLY, b_minus[0]), + ] + path = Path([p for c, p in patch_path], [c for c, p in patch_path]) + + return path, True + + + + +def _list_available_connectionstyles(connectors): + """ a helper function of the FancyArrowPatch to list the available + connection styles. It inspects the arguments of the __init__ methods of + each classes and report them + """ + import inspect + s = [] + for name, cls in connectors.items(): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + args_string = ["%s=%s" % (argname, str(argdefault)) \ + for argname, argdefault in zip(args[1:], defaults)] + s.append(",".join([name]+args_string)) + s.sort() + return s + +def _list_available_arrowstyles(transmuters): + """ a helper function of the FancyArrowPatch to list the available + arrow styles. It inspects the arguments of the __init__ methods of + each classes and report them + """ + import inspect + s = [] + for name, cls in transmuters.items(): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + args_string = ["%s=%s" % (argname, str(argdefault)) \ + for argname, argdefault in zip(args[1:], defaults)] + s.append(",".join([name]+args_string)) + s.sort() + return s + + + +class FancyArrowPatch(Patch): + """ + Draw a fancy arrow along a path. + + The "arrowstyle" argument determins what kind of + arrow will be drawn. In other words, it selects the + ArrowTransmuter class to use, and sets optional attributes. A + custom ArrowTransmuter can be used with arrow_transmuter argument + (should be an instance, not a class). mutation_scale determines + the overall size of the mutation (by which I mean the + transformation of the path to the fancy arrow) and the + mutation_aspect determines the aspect-ratio of the mutation. + + """ + + _fancy_arrow_transmuters = {"simple":SimpleArrowTransmuter, + "fancy":FancyArrowTransmuter, + "wedge":WedgeArrowTransmuter, + "-":CurveArrowTransmuter, + "->":CurveArrowBTransmuter, + "<-":CurveArrowATransmuter, + "<->":CurveArrowABTransmuter, + } + + _connectors = {"arc3":Arc3Connector, + "arc":ArcConnector, + "angle":AngleConnector, + "angle3":Angle3Connector, + } + + def __str__(self): + return self.__class__.__name__ \ + + "FancyArrowPatch(%g,%g,%g,%g,%g,%g)" % tuple(self._q_bezier) + + def __init__(self, posA=None, posB=None, + path=None, + arrowstyle="simple", + arrow_transmuter=None, + connectionstyle="arc3", + connector=None, + patchA=None, + patchB=None, + shrinkA=2., + shrinkB=2., + mutation_scale=1., + mutation_aspect=None, + **kwargs): + """ + If *posA* and *posB* is given, a path connecting two point are + created according to the connectionstyle. The path will be + clipped with *patchA* and *patchB* and further shirnked by + *shrinkA* and *shrinkB*. An arrow is drawn along this + resulting path using the *arrowstyle* parameter. If *path* + provided, an arrow is drawn along this path and *patchA*, + *patchB*, *shrinkA*, and *shrinkB* are ignored. + + The *connectionstyle* describes how *posA* and *posB* are + connected. It should be one of the available connectionstyle + names, with optional comma-separated attributes. Following + connection styles are available. + + %(AvailableConnectorstyles)s + + The connectionstyle name can be "custom", in which case the + *connector* needs to be set, which should be an instance + of ArrowTransmuterBase (or its derived). + + + The *arrowstyle* describes how the fancy arrow will be drawn. It + should be one of the available arrowstyle names, with optional + comma-separated attributes. These attributes are meant to be + scaled with the *mutation_scale*. Following arrow styles are + available. + + %(AvailableArrowstyles)s + + The arrowstyle name can be "custom", in which case the + arrow_transmuter needs to be set, which should be an instance + of ArrowTransmuterBase (or its derived). + + *mutation_scale* : a value with which attributes of arrowstyle + (e.g., head_length) will be scaled. default=1. + + *mutation_aspect* : The height of the rectangle will be + squeezed by this value before the mutation and the mutated + box will be stretched by the inverse of it. default=None. + + Valid kwargs are: + %(Patch)s + """ + + if posA is not None and posB is not None and path is None: + self._posA_posB = [posA, posB] + + if connectionstyle == "custom": + if connector is None: + raise ValueError("connector argument is needed with custom connectionstyle") + self.set_connector(connector) + else: + if connectionstyle is None: + connectionstyle = "arc3" + self.set_connectionstyle(connectionstyle) + + elif posA is None and posB is None and path is not None: + self._posA_posB = None + self._connetors = None + else: + raise ValueError("either posA and po... [truncated message content] |
From: <jd...@us...> - 2008-10-30 14:33:34
|
Revision: 6355 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6355&view=rev Author: jdh2358 Date: 2008-10-30 14:33:30 +0000 (Thu, 30 Oct 2008) Log Message: ----------- removed py2.6 incompat Modified Paths: -------------- trunk/matplotlib/doc/_templates/gallery.html trunk/matplotlib/doc/_templates/indexsidebar.html trunk/matplotlib/examples/pylab_examples/annotation_demo2.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/doc/_templates/gallery.html =================================================================== --- trunk/matplotlib/doc/_templates/gallery.html 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/doc/_templates/gallery.html 2008-10-30 14:33:30 UTC (rev 6355) @@ -63,6 +63,10 @@ <a href="examples/pylab_examples/alignment_test.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/alignment_test.png" border="0" alt="alignment_test"/></a> +<a href="examples/pylab_examples/annotation_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo2_00.png" border="0" alt="annotation_demo2"/></a> + +<a href="examples/pylab_examples/annotation_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo2_01.png" border="0" alt="annotation_demo2"/></a> + <a href="examples/pylab_examples/annotation_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo_00.png" border="0" alt="annotation_demo"/></a> <a href="examples/pylab_examples/annotation_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/annotation_demo_01.png" border="0" alt="annotation_demo"/></a> Modified: trunk/matplotlib/doc/_templates/indexsidebar.html =================================================================== --- trunk/matplotlib/doc/_templates/indexsidebar.html 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/doc/_templates/indexsidebar.html 2008-10-30 14:33:30 UTC (rev 6355) @@ -11,7 +11,7 @@ and mapping toolkit <a href="http://matplotlib.sf.net/basemap/doc/html">basemap</a>.</p> -<p>Please <a href="https://sourceforge.net/my/donations.php">donate</a> +<p>Please <a href="http://sourceforge.net/project/project_donations.php?group_id=80706">donate</a> to support matplotlib development.</p> <h3>Need help?</h3> Modified: trunk/matplotlib/examples/pylab_examples/annotation_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/examples/pylab_examples/annotation_demo2.py 2008-10-30 14:33:30 UTC (rev 6355) @@ -72,7 +72,7 @@ - fig.savefig('annotation_connection') + #fig.savefig('annotation_connection') if 1: @@ -82,7 +82,7 @@ el = Ellipse((2, -1), 0.5, 0.5) ax.add_patch(el) - + ax.annotate('$->$', xy=(2., -1), xycoords='data', xytext=(-150, -140), textcoords='offset points', bbox=dict(boxstyle="round", fc="0.8"), @@ -146,6 +146,6 @@ ) ) - fig.savefig('annotation_arrowstyle') + #fig.savefig('annotation_arrowstyle') show() Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 13:41:45 UTC (rev 6354) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-10-30 14:33:30 UTC (rev 6355) @@ -1512,10 +1512,10 @@ (x0, y0)] com = [Path.MOVETO, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, + Path.CURVE4, Path.CURVE4, Path.CURVE4, Path.CLOSEPOLY] path = Path(cp, com) @@ -1536,8 +1536,8 @@ BboxTransmuterBase.__init__(self) def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): - + # padding pad = mutation_size * self.pad @@ -1590,16 +1590,16 @@ left_saw_y = [y1] + \ [y1 - tooth_size2 - dsy*.5* i for i in range(dsy_n*2)] + \ [y0 + tooth_size2] - + saw_vertices = zip(bottom_saw_x, bottom_saw_y) + \ zip(right_saw_x, right_saw_y) + \ zip(top_saw_x, top_saw_y) + \ zip(left_saw_x, left_saw_y) + \ [(bottom_saw_x[0], bottom_saw_y[0])] - + return saw_vertices - + def transmute(self, x0, y0, width, height, mutation_size): saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) @@ -1926,7 +1926,7 @@ patch. Additionally the path can be shirnked by a fixed size (given in points) with shrinkA and shrinkB. """ - + class SimpleEvent: def __init__(self, xy): self.x, self.y = xy @@ -1938,7 +1938,7 @@ methods of each patch object is utilized to test if the point is inside the path. """ - + if patchA: def insideA(xy_display): #xy_display = patchA.get_data_transform().transform_point(xy_data) @@ -1949,7 +1949,7 @@ left, right = split_path_inout(path, insideA) except ValueError: right = path - + path = right if patchB: @@ -1969,10 +1969,10 @@ #def insideB(xy_data): # return ppp.contains_point(xy_data) ##return patchB.contains(ConnectorBase.SimpleEvent(xy))[0] - + return path - + def _shrink(self, path, shrinkA, shrinkB): """ Shrink the path by fixed size (in points) with shrinkA and shrinkB @@ -1992,7 +1992,7 @@ path = left return path - + def __call__(self, posA, posB, shrinkA=2., shrinkB=2., patchA=None, patchB=None): @@ -2000,7 +2000,7 @@ clipped_path = self._clip(path, patchA, patchB) shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) - + return shrinked_path @@ -2013,7 +2013,7 @@ """ def __init__(self, rad=0.): self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB @@ -2021,7 +2021,7 @@ dx, dy = x2 - x1, y2 - y1 f = self.rad - + cx, cy = x12 + f*dy, y12 - f*dx vertices = [(x1, y1), @@ -2037,7 +2037,7 @@ class Angle3Connector(ConnectorBase): """ Creates a simple quadratic bezier curve between two points. The middle control points is placed at the intersecting - point of two lines which crosses the start (or end) point + point of two lines which crosses the start (or end) point and has a angle of angleA (or angleB). """ def __init__(self, angleA=90, angleB=0): @@ -2049,9 +2049,9 @@ x2, y2 = posB cosA, sinA = math.cos(self.angleA/180.*math.pi),\ - math.sin(self.angleA/180.*math.pi), + math.sin(self.angleA/180.*math.pi), cosB, sinB = math.cos(self.angleB/180.*math.pi),\ - math.sin(self.angleB/180.*math.pi), + math.sin(self.angleB/180.*math.pi), cx, cy = get_intersection(x1, y1, cosA, sinA, x2, y2, cosB, sinB) @@ -2075,15 +2075,15 @@ self.angleB = angleB self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB cosA, sinA = math.cos(self.angleA/180.*math.pi),\ - math.sin(self.angleA/180.*math.pi), + math.sin(self.angleA/180.*math.pi), cosB, sinB = math.cos(self.angleB/180.*math.pi),\ - -math.sin(self.angleB/180.*math.pi), + -math.sin(self.angleB/180.*math.pi), cx, cy = get_intersection(x1, y1, cosA, sinA, x2, y2, cosB, sinB) @@ -2099,10 +2099,10 @@ (cx, cy), (cx + self.rad * cosB, cy + self.rad * sinB)]) codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3]) - + vertices.append((x2, y2)) codes.append(Path.LINETO) - + return Path(vertices, codes) @@ -2122,7 +2122,7 @@ self.armB = armB self.rad = rad - + def connect(self, posA, posB): x1, y1 = posA x2, y2 = posB @@ -2130,14 +2130,14 @@ vertices = [(x1, y1)] rounded = [] codes = [Path.MOVETO] - + if self.armA: cosA = math.cos(self.angleA/180.*math.pi) sinA = math.sin(self.angleA/180.*math.pi) #x_armA, y_armB d = self.armA - self.rad rounded.append((x1 + d*cosA, y1 + d*sinA)) - d = self.armA + d = self.armA rounded.append((x1 + d*cosA, y1 + d*sinA)) if self.armB: @@ -2149,7 +2149,7 @@ xp, yp = rounded[-1] dx, dy = x_armB - xp, y_armB - yp dd = (dx*dx + dy*dy)**.5 - + rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) vertices.extend(rounded) codes.extend([Path.LINETO, @@ -2163,7 +2163,7 @@ d = dd - self.rad rounded = [(xp + d*dx/dd, yp + d*dy/dd), (x_armB, y_armB)] - + if rounded: xp, yp = rounded[-1] dx, dy = x2 - xp, y2 - yp @@ -2177,7 +2177,7 @@ vertices.append((x2, y2)) codes.append(Path.LINETO) - + return Path(vertices, codes) @@ -2216,7 +2216,7 @@ assert segments[0][1] == Path.MOVETO assert segments[1][1] == Path.CURVE3 - + return list(segments[0][0]) + list(segments[1][0]) @@ -2284,7 +2284,7 @@ def _get_pad_projected(self, x0, y0, x1, y1, linewidth): # when no arrow head is drawn - + dx, dy = x0 - x1, y0 - y1 cp_distance = math.sqrt(dx**2 + dy**2) @@ -2298,7 +2298,7 @@ ddy = pady_projected * dy / cp_distance return ddx, ddy - + def _get_arrow_wedge(self, x0, y0, x1, y1, head_dist, cos_t, sin_t, linewidth ): @@ -2338,7 +2338,7 @@ return vertices_arrow, codes_arrow, ddx, ddy - + def transmute(self, path, mutation_size, linewidth): head_length, head_width = self.head_length * mutation_size, \ @@ -2360,7 +2360,7 @@ verticesA, codesA = [], [] #ddxA, ddyA = self._get_pad_projected(x1, y1, x0, y0, linewidth) ddxA, ddyA = 0., 0., #self._get_pad_projected(x1, y1, x0, y0, linewidth) - + # end arrow x2, y2 = path.vertices[-2] x3, y3 = path.vertices[-1] @@ -2443,8 +2443,8 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail + + # divide the path into a head and a tail head_length = self.head_length * mutation_size in_f = inside_circle(x2, y2, head_length) arrow_path = [(x0, y0), (x1, y1), (x2, y2)] @@ -2497,8 +2497,8 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail + + # divide the path into a head and a tail head_length = self.head_length * mutation_size arrow_path = [(x0, y0), (x1, y1), (x2, y2)] @@ -2510,7 +2510,7 @@ tolerence=0.01) path_head = path_in - # path for head + # path for head in_f = inside_circle(x2, y2, head_length*.8) path_out, path_in = \ split_bezier_intersecting_with_closedpath(arrow_path, @@ -2530,7 +2530,7 @@ tail_width*.5, w1=1., wm=0.6, w2=0.3) - # path for head + # path for head in_f = inside_circle(x0, y0, tail_width*.3) path_in, path_out = \ split_bezier_intersecting_with_closedpath(arrow_path, @@ -2593,7 +2593,7 @@ def transmute(self, path, mutation_size, linewidth): x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - + arrow_path = [(x0, y0), (x1, y1), (x2, y2)] b_plus, b_minus = make_wedged_bezier2(arrow_path, self.tail_width * mutation_size / 2., @@ -2676,12 +2676,12 @@ "angle":AngleConnector, "angle3":Angle3Connector, } - + def __str__(self): return self.__class__.__name__ \ + "FancyArrowPatch(%g,%g,%g,%g,%g,%g)" % tuple(self._q_bezier) - def __init__(self, posA=None, posB=None, + def __init__(self, posA=None, posB=None, path=None, arrowstyle="simple", arrow_transmuter=None, @@ -2752,7 +2752,7 @@ elif posA is None and posB is None and path is not None: self._posA_posB = None - self._connetors = None + self._connetors = None else: raise ValueError("either posA and posB, or path need to provided") @@ -2760,7 +2760,7 @@ self.patchB = patchB self.shrinkA = shrinkA self.shrinkB = shrinkB - + Patch.__init__(self, **kwargs) self._path_original = path @@ -2857,7 +2857,7 @@ """ Set the connector. - ACCEPTS: + ACCEPTS: """ self._connector = connector @@ -2897,7 +2897,7 @@ except KeyError: raise ValueError("Unknown Arrowstyle : %s" % arrowstyle_name) try: - arrowstyle_args_pair = [as.split("=") for as in as_list[1:]] + arrowstyle_args_pair = [a.split("=") for a in as_list[1:]] arrowstyle_args = dict([(k, float(v)) for k, v in arrowstyle_args_pair]) except ValueError: raise ValueError("Incorrect Arrowstyle argument : %s" % arrowstyle) @@ -2953,7 +2953,7 @@ """ _path = self.get_path_in_displaycoord() return self.get_transform().inverted().transform_path(_path) - + def get_path_in_displaycoord(self): """ Return the mutated path of the arrow in the display coord @@ -2972,16 +2972,16 @@ _path = self.get_transform().transform_path(self._path_original) - + _path, closed = self.get_arrow_transmuter()(_path, self.get_mutation_scale(), self.get_linewidth(), self.get_mutation_aspect() ) - + if not closed: self.fill = False - + return _path This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-11-07 14:52:19
|
Revision: 6372 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6372&view=rev Author: mdboom Date: 2008-11-07 14:52:04 +0000 (Fri, 07 Nov 2008) Log Message: ----------- Committed Andrew Straw's patch to support hyperlinks. Currently only the SVG backend, but the infrastructure is there for other backends to support it. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/artist.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/backends/backend_ps.py trunk/matplotlib/lib/matplotlib/backends/backend_svg.py trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/lib/matplotlib/image.py trunk/matplotlib/lib/matplotlib/patches.py trunk/matplotlib/lib/matplotlib/text.py trunk/matplotlib/src/_backend_agg.cpp Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/hyperlinks.py Added: trunk/matplotlib/examples/pylab_examples/hyperlinks.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/hyperlinks.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/hyperlinks.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- noplot -*- + +""" +This example demonstrates how to set a hyperlinks on various kinds of elements. + +This currently only works with the SVG backend. +""" + +import numpy as np +import matplotlib.cm as cm +import matplotlib.mlab as mlab +import matplotlib.pyplot as plt + +f = plt.figure() +s = plt.scatter([1,2,3],[4,5,6]) +s.set_urls(['http://www.bbc.co.uk/news','http://www.google.com',None]) +f.canvas.print_figure('scatter.svg') + +f = plt.figure() +delta = 0.025 +x = y = np.arange(-3.0, 3.0, delta) +X, Y = np.meshgrid(x, y) +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 = Z2-Z1 # difference of Gaussians + +im = plt.imshow(Z, interpolation='bilinear', cmap=cm.gray, + origin='lower', extent=[-3,3,-3,3]) + +im.set_url('http://www.google.com') +f.canvas.print_figure('image.svg') + Modified: trunk/matplotlib/lib/matplotlib/artist.py =================================================================== --- trunk/matplotlib/lib/matplotlib/artist.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/artist.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -50,6 +50,7 @@ self._propobservers = {} # a dict from oids to funcs self.axes = None self._remove_method = None + self._url = None def remove(self): """ @@ -313,6 +314,18 @@ """ return self.figure is not None + def get_url(self): + """ + Returns the url + """ + return self._url + + def set_url(self, url): + """ + Sets the url for the artist + """ + self._url = url + def get_figure(self): """ Return the :class:`~matplotlib.figure.Figure` instance the Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -5480,7 +5480,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=1.0, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=1, - filterrad=4.0, imlim=None, resample=None, **kwargs): + filterrad=4.0, imlim=None, resample=None, url=None, **kwargs): """ call signature:: @@ -5601,6 +5601,7 @@ im.set_clim(vmin, vmax) else: im.autoscale_None() + im.set_url(url) xmin, xmax, ymin, ymax = im.get_extent() Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -107,7 +107,7 @@ def draw_path_collection(self, master_transform, cliprect, clippath, clippath_trans, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds): + linestyles, antialiaseds, urls): """ Draws a collection of paths, selecting drawing properties from the lists *facecolors*, *edgecolors*, *linewidths*, @@ -136,7 +136,7 @@ for xo, yo, path_id, gc, rgbFace in self._iter_collection( path_ids, cliprect, clippath, clippath_trans, offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds): + linewidths, linestyles, antialiaseds, urls): path, transform = path_id transform = transforms.Affine2D(transform.get_matrix()).translate(xo, yo) self.draw_path(gc, path, transform, rgbFace) @@ -164,7 +164,7 @@ return self.draw_path_collection( master_transform, cliprect, clippath, clippath_trans, paths, [], offsets, offsetTrans, facecolors, edgecolors, - linewidths, [], [antialiased]) + linewidths, [], [antialiased], [None]) def _iter_collection_raw_paths(self, master_transform, paths, all_transforms): """ @@ -198,7 +198,7 @@ def _iter_collection(self, path_ids, cliprect, clippath, clippath_trans, offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds): + linewidths, linestyles, antialiaseds, urls): """ This is a helper method (along with :meth:`_iter_collection_raw_paths`) to make it easier to write @@ -232,6 +232,7 @@ Nlinewidths = len(linewidths) Nlinestyles = len(linestyles) Naa = len(antialiaseds) + Nurls = len(urls) if (Nfacecolors == 0 and Nedgecolors == 0) or Npaths == 0: return @@ -268,6 +269,9 @@ gc.set_alpha(rgbFace[-1]) rgbFace = rgbFace[:3] gc.set_antialiased(antialiaseds[i % Naa]) + + if Nurls: + gc.set_url(urls[i % Nurls]) yield xo, yo, path_id, gc, rgbFace @@ -433,6 +437,7 @@ self._linewidth = 1 self._rgb = (0.0, 0.0, 0.0) self._hatch = None + self._url = None def copy_properties(self, gc): 'Copy properties from gc to self' @@ -447,6 +452,7 @@ self._linewidth = gc._linewidth self._rgb = gc._rgb self._hatch = gc._hatch + self._url = gc._url def get_alpha(self): """ @@ -521,6 +527,12 @@ matlab format string, a html hex color string, or a rgb tuple """ return self._rgb + + def get_url(self): + """ + returns a url if one is set, None otherwise + """ + return self._url def set_alpha(self, alpha): """ @@ -621,6 +633,12 @@ raise ValueError('Unrecognized linestyle: %s' % style) self._linestyle = style self.set_dashes(offset, dashes) + + def set_url(self, url): + """ + Sets the url for links in compatible backends + """ + self._url = url def set_hatch(self, hatch): """ Modified: trunk/matplotlib/lib/matplotlib/backends/backend_ps.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/backends/backend_ps.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -531,7 +531,7 @@ def draw_path_collection(self, master_transform, cliprect, clippath, clippath_trans, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds): + linestyles, antialiaseds, urls): write = self._pswriter.write path_codes = [] @@ -548,7 +548,7 @@ for xo, yo, path_id, gc, rgbFace in self._iter_collection( path_codes, cliprect, clippath, clippath_trans, offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds): + linewidths, linestyles, antialiaseds, urls): ps = "%g %g %s" % (xo, yo, path_id) self._draw_ps(ps, gc, rgbFace) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -67,9 +67,13 @@ else: clippath = 'clip-path="url(#%s)"' % clipid + if gc.get_url() is not None: + self._svgwriter.write('<a xlink:href="%s">' % gc.get_url()) style = self._get_style(gc, rgbFace) self._svgwriter.write ('<%s style="%s" %s %s/>\n' % ( element, style, clippath, details)) + if gc.get_url() is not None: + self._svgwriter.write('</a>') def _get_font(self, prop): key = hash(prop) @@ -224,7 +228,7 @@ def draw_path_collection(self, master_transform, cliprect, clippath, clippath_trans, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds): + linestyles, antialiaseds, urls): write = self._svgwriter.write path_codes = [] @@ -242,8 +246,11 @@ for xo, yo, path_id, gc, rgbFace in self._iter_collection( path_codes, cliprect, clippath, clippath_trans, offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds): + linewidths, linestyles, antialiaseds, urls): clipid = self._get_gc_clip_svg(gc) + url = gc.get_url() + if url is not None: + self._svgwriter.write('<a xlink:href="%s">' % url) if clipid is not None: write('<g clip-path="url(#%s)">' % clipid) details = 'xlink:href="#%s" x="%f" y="%f"' % (path_id, xo, self.height - yo) @@ -251,6 +258,8 @@ self._svgwriter.write ('<use style="%s" %s/>\n' % (style, details)) if clipid is not None: write('</g>') + if url is not None: + self._svgwriter.write('</a>') self._path_collection_id += 1 @@ -274,6 +283,9 @@ h,w = im.get_size_out() + url = getattr(im, '_url', None) + if url is not None: + self._svgwriter.write('<a xlink:href="%s">' % url) self._svgwriter.write ( '<image x="%f" y="%f" width="%f" height="%f" ' '%s xlink:href="'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, transstr) @@ -298,6 +310,8 @@ self._svgwriter.write(filename) self._svgwriter.write('"/>\n') + if url is not None: + self._svgwriter.write('</a>') def draw_text(self, gc, x, y, s, prop, angle, ismath): if ismath: Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -71,6 +71,7 @@ norm = None, # optional for ScalarMappable cmap = None, # ditto pickradius = 5.0, + urls = None, **kwargs ): """ @@ -86,6 +87,7 @@ self.set_linewidth(linewidths) self.set_linestyle(linestyles) self.set_antialiased(antialiaseds) + self.set_urls(urls) self._uniform_offsets = None self._offsets = np.array([], np.float_) @@ -203,7 +205,7 @@ paths, self.get_transforms(), offsets, transOffset, self.get_facecolor(), self.get_edgecolor(), self._linewidths, - self._linestyles, self._antialiaseds) + self._linestyles, self._antialiaseds, self._urls) renderer.close_group(self.__class__.__name__) def contains(self, mouseevent): @@ -227,6 +229,14 @@ def set_pickradius(self,pickradius): self.pickradius = 5 def get_pickradius(self): return self.pickradius + def set_urls(self, urls): + if urls is None: + self._urls = [None,] + else: + self._urls = urls + + def get_urls(self): return self._urls + def set_offsets(self, offsets): """ Set the offsets for the collection. *offsets* can be a scalar Modified: trunk/matplotlib/lib/matplotlib/image.py =================================================================== --- trunk/matplotlib/lib/matplotlib/image.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/image.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -88,13 +88,10 @@ self.set_filterrad(filterrad) self._filterrad = filterrad - - self.set_interpolation(interpolation) self.set_resample(resample) self.axes = ax - self._imcache = None self.update(kwargs) @@ -234,9 +231,11 @@ self.axes.get_yscale() != 'linear'): warnings.warn("Images are not supported on non-linear axes.") im = self.make_image(renderer.get_image_magnification()) + im._url = self.get_url() l, b, widthDisplay, heightDisplay = self.axes.bbox.bounds + clippath, affine = self.get_transformed_clip_path_and_affine() renderer.draw_image(round(l), round(b), im, self.axes.bbox.frozen(), - *self.get_transformed_clip_path_and_affine()) + clippath, affine) def contains(self, mouseevent): """Test whether the mouse event occured within the image. Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -278,6 +278,7 @@ gc.set_antialiased(self._antialiased) self._set_gc_clip(gc) gc.set_capstyle('projecting') + gc.set_url(self._url) if (not self.fill or self._facecolor is None or (cbook.is_string_like(self._facecolor) and self._facecolor.lower()=='none')): Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/lib/matplotlib/text.py 2008-11-07 14:52:04 UTC (rev 6372) @@ -464,6 +464,7 @@ gc = renderer.new_gc() gc.set_foreground(self._color) gc.set_alpha(self._alpha) + gc.set_url(self._url) if self.get_clip_on(): gc.set_clip_rectangle(self.clipbox) Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-11-07 13:31:25 UTC (rev 6371) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-11-07 14:52:04 UTC (rev 6372) @@ -1154,7 +1154,7 @@ Py::Object RendererAgg::draw_path_collection(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_path_collection"); - args.verify_length(13); + args.verify_length(14); //segments, trans, clipbox, colors, linewidths, antialiaseds agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[0]); @@ -1170,7 +1170,8 @@ Py::SeqBase<Py::Float> linewidths = args[10]; Py::SeqBase<Py::Object> linestyles_obj = args[11]; Py::SeqBase<Py::Int> antialiaseds = args[12]; - + // We don't actually care about urls for Agg, so just ignore it. + // Py::SeqBase<Py::Object> urls = args[13]; PathListGenerator path_generator(paths); try { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-09 00:09:26
|
Revision: 6378 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6378&view=rev Author: jdh2358 Date: 2008-11-09 00:09:23 +0000 (Sun, 09 Nov 2008) Log Message: ----------- added jae joons layer images patch Modified Paths: -------------- trunk/matplotlib/doc/_templates/gallery.html trunk/matplotlib/doc/faq/environment_variables_faq.rst trunk/matplotlib/doc/faq/index.rst trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/mlab.py Modified: trunk/matplotlib/doc/_templates/gallery.html =================================================================== --- trunk/matplotlib/doc/_templates/gallery.html 2008-11-08 18:41:25 UTC (rev 6377) +++ trunk/matplotlib/doc/_templates/gallery.html 2008-11-09 00:09:23 UTC (rev 6378) @@ -159,8 +159,6 @@ <a href="examples/pylab_examples/customize_rc.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/customize_rc.png" border="0" alt="customize_rc"/></a> -<a href="examples/pylab_examples/dannys_example.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/dannys_example.png" border="0" alt="dannys_example"/></a> - <a href="examples/pylab_examples/dash_control.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/dash_control.png" border="0" alt="dash_control"/></a> <a href="examples/pylab_examples/dashpointlabel.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/dashpointlabel.png" border="0" alt="dashpointlabel"/></a> @@ -241,8 +239,6 @@ <a href="examples/pylab_examples/ganged_plots.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/ganged_plots.png" border="0" alt="ganged_plots"/></a> -<a href="examples/pylab_examples/geo_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/geo_demo.png" border="0" alt="geo_demo"/></a> - <a href="examples/pylab_examples/gradient_bar.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/gradient_bar.png" border="0" alt="gradient_bar"/></a> <a href="examples/pylab_examples/griddata_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/griddata_demo.png" border="0" alt="griddata_demo"/></a> @@ -457,8 +453,6 @@ <a href="examples/pylab_examples/step_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/step_demo.png" border="0" alt="step_demo"/></a> -<a href="examples/pylab_examples/stix_fonts_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/stix_fonts_demo.png" border="0" alt="stix_fonts_demo"/></a> - <a href="examples/pylab_examples/subplot_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/subplot_demo.png" border="0" alt="subplot_demo"/></a> <a href="examples/pylab_examples/subplot_toolbar.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/subplot_toolbar_00.png" border="0" alt="subplot_toolbar"/></a> @@ -467,12 +461,8 @@ <a href="examples/pylab_examples/subplots_adjust.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/subplots_adjust.png" border="0" alt="subplots_adjust"/></a> -<a href="examples/pylab_examples/symlog_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/symlog_demo.png" border="0" alt="symlog_demo"/></a> - <a href="examples/pylab_examples/table_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/table_demo.png" border="0" alt="table_demo"/></a> -<a href="examples/pylab_examples/tex_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/tex_demo.png" border="0" alt="tex_demo"/></a> - <a href="examples/pylab_examples/text_handles.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/text_handles.png" border="0" alt="text_handles"/></a> <a href="examples/pylab_examples/text_rotation.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/text_rotation.png" border="0" alt="text_rotation"/></a> @@ -487,8 +477,6 @@ <a href="examples/pylab_examples/unicode_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/unicode_demo.png" border="0" alt="unicode_demo"/></a> -<a href="examples/pylab_examples/usetex_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/usetex_demo.png" border="0" alt="usetex_demo"/></a> - <a href="examples/pylab_examples/vertical_ticklabels.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/vertical_ticklabels.png" border="0" alt="vertical_ticklabels"/></a> <a href="examples/pylab_examples/vline_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/vline_demo.png" border="0" alt="vline_demo"/></a> Modified: trunk/matplotlib/doc/faq/environment_variables_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/environment_variables_faq.rst 2008-11-08 18:41:25 UTC (rev 6377) +++ trunk/matplotlib/doc/faq/environment_variables_faq.rst 2008-11-09 00:09:23 UTC (rev 6378) @@ -1,4 +1,4 @@ -.. _environment-variablesg: +.. _environment-variables: ********************* Environment Variables Modified: trunk/matplotlib/doc/faq/index.rst =================================================================== --- trunk/matplotlib/doc/faq/index.rst 2008-11-08 18:41:25 UTC (rev 6377) +++ trunk/matplotlib/doc/faq/index.rst 2008-11-09 00:09:23 UTC (rev 6378) @@ -15,6 +15,6 @@ :maxdepth: 2 installing_faq.rst + usage.rst + howto_faq.rst troubleshooting_faq.rst - howto_faq.rst - environment_variables_faq.rst Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-08 18:41:25 UTC (rev 6377) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:09:23 UTC (rev 6378) @@ -1536,15 +1536,16 @@ ims = [(im.make_image(mag),0,0) for im in self.images if im.get_visible()] - + #flip the images if their origin is "upper" + [im.flipud_out() for _im, (im,_,_) in zip(self.images, ims) \ + if _im.origin=="upper"] + l, b, r, t = self.bbox.extents width = mag*((round(r) + 0.5) - (round(l) - 0.5)) height = mag*((round(t) + 0.5) - (round(b) - 0.5)) im = mimage.from_images(height, width, ims) - if self.images[0].origin=='upper': - im.flipud_out() im.is_grayscale = False l, b, w, h = self.bbox.bounds Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-08 18:41:25 UTC (rev 6377) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-09 00:09:23 UTC (rev 6378) @@ -2013,6 +2013,7 @@ output to a new attribute name *outname*. The returned record array is identical to *r*, with extra arrays for each element in *summaryfuncs*. + """ names = list(r.dtype.names) @@ -2569,7 +2570,19 @@ length = max(len(colname),np.max(map(len,map(str,column)))) return 1, length+padding, "%d" # right justify - if ntype==np.float or ntype==np.float32 or ntype==np.float64 or ntype==np.float96 or ntype==np.float_: + # JDH: my powerbook does not have np.float96 using np 1.3.0 + """ + In [2]: np.__version__ + Out[2]: '1.3.0.dev5948' + + In [3]: !uname -a + Darwin Macintosh-5.local 9.4.0 Darwin Kernel Version 9.4.0: Mon Jun 9 19:30:53 PDT 2008; root:xnu-1228.5.20~1/RELEASE_I386 i386 i386 + + In [4]: np.float96 + --------------------------------------------------------------------------- + AttributeError Traceback (most recent call la + """ + if ntype==np.float or ntype==np.float32 or ntype==np.float64 or (hasattr(np, 'float96') and (ntype==np.float96)) or ntype==np.float_: fmt = "%." + str(precision) + "f" length = max(len(colname),np.max(map(len,map(lambda x:fmt%x,column)))) return 1, length+padding, fmt # right justify This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-09 00:18:49
|
Revision: 6379 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6379&view=rev Author: jdh2358 Date: 2008-11-09 00:18:40 +0000 (Sun, 09 Nov 2008) Log Message: ----------- commited jae joons 2nd patch; figimage still sems broken for origin=up Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/figimage_demo.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/figure.py Modified: trunk/matplotlib/examples/pylab_examples/figimage_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/figimage_demo.py 2008-11-09 00:09:23 UTC (rev 6378) +++ trunk/matplotlib/examples/pylab_examples/figimage_demo.py 2008-11-09 00:18:40 UTC (rev 6379) @@ -12,9 +12,12 @@ Z.shape = 100,100 Z[:,50:] = 1. -im1 = plt.figimage(Z, xo=50, yo=0, cmap=cm.jet) -im2 = plt.figimage(Z, xo=100, yo=100, alpha=.8, cmap=cm.jet) +im1 = plt.figimage(Z, xo=50, yo=0, cmap=cm.jet, origin='upper') +im2 = plt.figimage(Z, xo=100, yo=100, alpha=.8, cmap=cm.jet, origin='upper') +fig.savefig('figimage_demo.png') +fig.savefig('figimage_demo.svg') +fig.savefig('figimage_demo.pdf') plt.show() Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:09:23 UTC (rev 6378) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:18:40 UTC (rev 6379) @@ -1537,9 +1537,9 @@ for im in self.images if im.get_visible()] #flip the images if their origin is "upper" - [im.flipud_out() for _im, (im,_,_) in zip(self.images, ims) \ - if _im.origin=="upper"] - + if self.images[0].origin=='upper': + im.flipud_out() + l, b, r, t = self.bbox.extents width = mag*((round(r) + 0.5) - (round(l) - 0.5)) height = mag*((round(t) + 0.5) - (round(b) - 0.5)) Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2008-11-09 00:09:23 UTC (rev 6378) +++ trunk/matplotlib/lib/matplotlib/figure.py 2008-11-09 00:18:40 UTC (rev 6379) @@ -752,11 +752,15 @@ mag = renderer.get_image_magnification() ims = [(im.make_image(mag), im.ox*mag, im.oy*mag) for im in self.images] + + for _im, (im,_,_) in zip(self.images, ims): + if _im.origin=="upper": + im.flipud_out() + + im = _image.from_images(self.bbox.height * mag, self.bbox.width * mag, ims) - if self.images[0].origin=='upper': - im.flipud_out() im.is_grayscale = False l, b, w, h = self.bbox.bounds This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-09 00:24:32
|
Revision: 6380 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6380&view=rev Author: jdh2358 Date: 2008-11-09 00:24:29 +0000 (Sun, 09 Nov 2008) Log Message: ----------- commited jae joons 2nd patch; figimage still sems broken for origin=up Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/layer_images.py trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/examples/pylab_examples/layer_images.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/layer_images.py 2008-11-09 00:18:40 UTC (rev 6379) +++ trunk/matplotlib/examples/pylab_examples/layer_images.py 2008-11-09 00:24:29 UTC (rev 6380) @@ -27,17 +27,19 @@ extent = xmin, xmax, ymin, ymax Z1 = array(([0,1]*4 + [1,0]*4)*4); Z1.shape = 8,8 # chessboard im1 = imshow(Z1, cmap=cm.gray, interpolation='nearest', - extent=extent) + extent=extent, origin='lower') hold(True) Z2 = func3(X, Y) im2 = imshow(Z2, cmap=cm.jet, alpha=.9, interpolation='bilinear', - extent=extent) + extent=extent, origin='lower') #axis([xmin, xmax, ymin, ymax]) -#savefig('layer_images') +savefig('layer_images.png') +savefig('layer_images.svg') +savefig('layer_images.pdf') show() Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:18:40 UTC (rev 6379) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:24:29 UTC (rev 6380) @@ -1537,9 +1537,11 @@ for im in self.images if im.get_visible()] #flip the images if their origin is "upper" - if self.images[0].origin=='upper': - im.flipud_out() + for _im, (im,_,_) in zip(self.images, ims): + if _im.origin=="upper": + im.flipud_out() + l, b, r, t = self.bbox.extents width = mag*((round(r) + 0.5) - (round(l) - 0.5)) height = mag*((round(t) + 0.5) - (round(b) - 0.5)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-09 13:16:43
|
Revision: 6381 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6381&view=rev Author: jdh2358 Date: 2008-11-09 13:16:39 +0000 (Sun, 09 Nov 2008) Log Message: ----------- fixed from_images to respect stride Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/figimage_demo.py trunk/matplotlib/examples/pylab_examples/layer_images.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/figure.py trunk/matplotlib/src/_image.cpp Modified: trunk/matplotlib/examples/pylab_examples/figimage_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/figimage_demo.py 2008-11-09 00:24:29 UTC (rev 6380) +++ trunk/matplotlib/examples/pylab_examples/figimage_demo.py 2008-11-09 13:16:39 UTC (rev 6381) @@ -12,12 +12,12 @@ Z.shape = 100,100 Z[:,50:] = 1. -im1 = plt.figimage(Z, xo=50, yo=0, cmap=cm.jet, origin='upper') -im2 = plt.figimage(Z, xo=100, yo=100, alpha=.8, cmap=cm.jet, origin='upper') +im1 = plt.figimage(Z, xo=50, yo=0, cmap=cm.jet, origin='lower') +im2 = plt.figimage(Z, xo=100, yo=100, alpha=.8, cmap=cm.jet, origin='lower') -fig.savefig('figimage_demo.png') -fig.savefig('figimage_demo.svg') -fig.savefig('figimage_demo.pdf') +#fig.savefig('figimage_demo.png') +#fig.savefig('figimage_demo.svg') +#fig.savefig('figimage_demo.pdf') plt.show() Modified: trunk/matplotlib/examples/pylab_examples/layer_images.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/layer_images.py 2008-11-09 00:24:29 UTC (rev 6380) +++ trunk/matplotlib/examples/pylab_examples/layer_images.py 2008-11-09 13:16:39 UTC (rev 6381) @@ -25,15 +25,17 @@ xmin, xmax, ymin, ymax = amin(x), amax(x), amin(y), amax(y) extent = xmin, xmax, ymin, ymax +fig = plt.figure(frameon=False) + Z1 = array(([0,1]*4 + [1,0]*4)*4); Z1.shape = 8,8 # chessboard im1 = imshow(Z1, cmap=cm.gray, interpolation='nearest', - extent=extent, origin='lower') + extent=extent) hold(True) Z2 = func3(X, Y) im2 = imshow(Z2, cmap=cm.jet, alpha=.9, interpolation='bilinear', - extent=extent, origin='lower') + extent=extent) #axis([xmin, xmax, ymin, ymax]) Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 00:24:29 UTC (rev 6380) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-09 13:16:39 UTC (rev 6381) @@ -1536,12 +1536,7 @@ ims = [(im.make_image(mag),0,0) for im in self.images if im.get_visible()] - #flip the images if their origin is "upper" - for _im, (im,_,_) in zip(self.images, ims): - if _im.origin=="upper": - im.flipud_out() - l, b, r, t = self.bbox.extents width = mag*((round(r) + 0.5) - (round(l) - 0.5)) height = mag*((round(t) + 0.5) - (round(b) - 0.5)) Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2008-11-09 00:24:29 UTC (rev 6380) +++ trunk/matplotlib/lib/matplotlib/figure.py 2008-11-09 13:16:39 UTC (rev 6381) @@ -753,11 +753,6 @@ ims = [(im.make_image(mag), im.ox*mag, im.oy*mag) for im in self.images] - for _im, (im,_,_) in zip(self.images, ims): - if _im.origin=="upper": - im.flipud_out() - - im = _image.from_images(self.bbox.height * mag, self.bbox.width * mag, ims) Modified: trunk/matplotlib/src/_image.cpp =================================================================== --- trunk/matplotlib/src/_image.cpp 2008-11-09 00:24:29 UTC (rev 6380) +++ trunk/matplotlib/src/_image.cpp 2008-11-09 13:16:39 UTC (rev 6381) @@ -104,7 +104,9 @@ args.verify_length(0); int stride = rbufOut->stride(); + //std::cout << "flip before: " << rbufOut->stride() << std::endl; rbufOut->attach(bufferOut, colsOut, rowsOut, -stride); + //std::cout << "flip after: " << rbufOut->stride() << std::endl; return Py::Object(); } @@ -744,18 +746,24 @@ rb.clear(agg::rgba(1, 1, 1, 1)); - for (size_t imnum=0; imnum< N; imnum++) { tup = Py::Tuple(tups[imnum]); Image* thisim = static_cast<Image*>(tup[0].ptr()); ox = Py::Int(tup[1]); oy = Py::Int(tup[2]); - + bool isflip = (thisim->rbufOut->stride())<0; + //std::cout << "from images " << isflip << "; stride=" << thisim->rbufOut->stride() << std::endl; size_t ind=0; for (size_t j=0; j<thisim->rowsOut; j++) { for (size_t i=0; i<thisim->colsOut; i++) { thisx = i+ox; - thisy = j+oy; + + if (isflip) + thisy = thisim->rowsOut - j + oy; + else + thisy = j+oy; + + if (thisx>=numcols || thisy>=numrows) { ind +=4; continue; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2008-11-09 14:00:50
|
Revision: 6383 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6383&view=rev Author: jouni Date: 2008-11-09 14:00:49 +0000 (Sun, 09 Nov 2008) Log Message: ----------- Fix a possible EINTR problem in dviread Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/dviread.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-09 13:17:41 UTC (rev 6382) +++ trunk/matplotlib/CHANGELOG 2008-11-09 14:00:49 UTC (rev 6383) @@ -1,3 +1,6 @@ +2008-11-09 Fix a possible EINTR problem in dviread, which might help + when saving pdf files from the qt backend. - JKS + 2008-10-24 Added Jae Joon's fancy arrow, box and annotation enhancements -- see examples/pylab_examples/annotation_demo2.py Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2008-11-09 13:17:41 UTC (rev 6382) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2008-11-09 14:00:49 UTC (rev 6383) @@ -689,7 +689,9 @@ def __init__(self, filename): file = open(filename, 'rt') try: + matplotlib.verbose.report('Parsing TeX encoding ' + filename, 'debug-annoying') self.encoding = self._parse(file) + matplotlib.verbose.report('Result: ' + `self.encoding`, 'debug-annoying') finally: file.close() @@ -746,15 +748,33 @@ assert "'" not in filename cmd += "'" + filename + "'" + matplotlib.verbose.report('find_tex_file(%s): %s' \ + % (filename,cmd), 'debug') pipe = os.popen(cmd, 'r') - result = pipe.readline().rstrip() + result = "" + while True: + data = _read_nointr(pipe) + if data == "": + break + result += data pipe.close() + result = result.rstrip() - matplotlib.verbose.report('find_tex_file: %s -> %s' \ - % (filename, result), + matplotlib.verbose.report('find_tex_file result: %s' % result, 'debug') return result +def _read_nointr(pipe, bufsize=-1): + while True: + try: + return pipe.read(bufsize) + except OSError, e: + if e.errno == errno.EINTR: + continue + else: + raise + + # With multiple text objects per figure (e.g. tick labels) we may end # up reading the same tfm and vf files many times, so we implement a # simple cache. TODO: is this worth making persistent? This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-11-11 06:53:00
|
Revision: 6387 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6387&view=rev Author: efiring Date: 2008-11-11 06:52:52 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Fix handling of c kwarg in scatter, with array of strings; modification of patch by Ryan May. The fix involves making cbook.is_string_like handle variables of type numpy.string_. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/cbook.py trunk/matplotlib/lib/matplotlib/colors.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-10 22:41:27 UTC (rev 6386) +++ trunk/matplotlib/CHANGELOG 2008-11-11 06:52:52 UTC (rev 6387) @@ -1,4 +1,8 @@ -2008-11-09 Fix a possible EINTR problem in dviread, which might help +2008-11-10 Fix handling of c kwarg by scatter; generalize + is_string_like to accept numpy and numpy.ma string + array scalars. - RM and EF + +2008-11-09 Fix a possible EINTR problem in dviread, which might help when saving pdf files from the qt backend. - JKS 2008-10-24 Added Jae Joon's fancy arrow, box and annotation Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-10 22:41:27 UTC (rev 6386) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 06:52:52 UTC (rev 6387) @@ -4927,17 +4927,17 @@ x, y, s, c = cbook.delete_masked_points(x, y, s, c) - # The inherent ambiguity is resolved in favor of color - # mapping, not interpretation as rgb or rgba. - if not is_string_like(c): + if is_string_like(c) or cbook.is_sequence_of_strings(c): + colors = mcolors.colorConverter.to_rgba_array(c, alpha) + else: sh = np.shape(c) + # The inherent ambiguity is resolved in favor of color + # mapping, not interpretation as rgb or rgba: if len(sh) == 1 and sh[0] == len(x): colors = None # use cmap, norm after collection is created else: colors = mcolors.colorConverter.to_rgba_array(c, alpha) - else: - colors = mcolors.colorConverter.to_rgba_array(c, alpha) if not iterable(s): scales = (s,) Modified: trunk/matplotlib/lib/matplotlib/cbook.py =================================================================== --- trunk/matplotlib/lib/matplotlib/cbook.py 2008-11-10 22:41:27 UTC (rev 6386) +++ trunk/matplotlib/lib/matplotlib/cbook.py 2008-11-11 06:52:52 UTC (rev 6387) @@ -266,8 +266,14 @@ def is_string_like(obj): - 'return true if *obj* looks like a string' - if hasattr(obj, 'shape'): return False + 'Return True if *obj* looks like a string' + if isinstance(obj, (str, unicode)): return True + # numpy strings are subclass of str, ma strings are not + if ma.isMaskedArray(obj): + if obj.ndim == 0 and obj.dtype.kind in 'SU': + return True + else: + return False try: obj + '' except (TypeError, ValueError): return False return True Modified: trunk/matplotlib/lib/matplotlib/colors.py =================================================================== --- trunk/matplotlib/lib/matplotlib/colors.py 2008-11-10 22:41:27 UTC (rev 6386) +++ trunk/matplotlib/lib/matplotlib/colors.py 2008-11-11 06:52:52 UTC (rev 6387) @@ -320,11 +320,9 @@ def to_rgba_array(self, c, alpha=None): """ - Returns an Numpy array of *RGBA* tuples. + Returns a numpy array of *RGBA* tuples. Accepts a single mpl color spec or a sequence of specs. - If the sequence is a list or array, the items are changed in place, - but an array copy is still returned. Special case to handle "no color": if *c* is "none" (case-insensitive), then an empty array will be returned. Same for an empty list. @@ -339,11 +337,8 @@ try: result = np.array([self.to_rgba(c, alpha)], dtype=np.float_) except ValueError: - # If c is a list it must be maintained as the same list - # with modified items so that items can be appended to - # it. This is needed for examples/dynamic_collections.py. if isinstance(c, np.ndarray): - if len(c.shape) != 2: + if c.ndim != 2 and c.dtype.kind not in 'SU': raise ValueError("Color array must be two-dimensional") result = np.zeros((len(c), 4)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 16:30:08
|
Revision: 6389 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6389&view=rev Author: ryanmay Date: 2008-11-11 16:30:03 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Add 'pad_to' and 'sides' parameters to mlab.psd() to allow controlling of zero padding and returning of negative frequency components, respecitively. These are added in a way that does not change the API. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/mlab.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 08:04:24 UTC (rev 6388) +++ trunk/matplotlib/CHANGELOG 2008-11-11 16:30:03 UTC (rev 6389) @@ -1,3 +1,8 @@ +2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to + allow controlling of zero padding and returning of + negative frequency components, respecitively. These are + added in a way that does not change the API. - RM + 2008-11-10 Fix handling of c kwarg by scatter; generalize is_string_like to accept numpy and numpy.ma string array scalars. - RM and EF Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 08:04:24 UTC (rev 6388) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 16:30:03 UTC (rev 6389) @@ -239,7 +239,7 @@ return y - (b*x + a) def psd(x, NFFT=256, Fs=2, detrend=detrend_none, - window=window_hanning, noverlap=0): + window=window_hanning, noverlap=0, pad_to=None, sides='default'): """ The power spectral density by Welch's average periodogram method. The vector *x* is divided into *NFFT* length blocks. Each block @@ -273,22 +273,36 @@ :func:`numpy.blackman`, :func:`numpy.hamming`, :func:`numpy.bartlett`, :func:`scipy.signal`, :func:`scipy.signal.get_window`, etc. The default is - :func:`window_hanning`. + :func:`window_hanning`. If a function is passed as the + argument, it must take a data segment as an argument and + return the windowed version of the segment. *noverlap* The number of points of overlap between blocks. The default value is 0 (no overlap). + *pad_to* + The number of points to which the data segment is padd when + performing the FFT. This can be different from *NFFT*, which + specifies the number of data points used. While not increasing + the actual resolution of the psd (the minimum distance between + resolvable peaks), this can give more points in the plot, + allowing for more detail. This corresponds to the *n* parameter + in the call to fft(). The default is None, which sets *pad_to* + equal to *NFFT* + + *sides* [ 'default' | 'onesided' | 'twosided' ] + Specifies which sides of the PSD to return. Default gives the + default behavior, which returns one-sided for real data and both + for complex data. 'one' forces the return of a one-sided PSD, while + 'both' forces two-sided. + Returns the tuple (*Pxx*, *freqs*). Refs: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) """ - # I think we could remove this condition without hurting anything. - if NFFT % 2: - raise ValueError('NFFT must be even') - x = np.asarray(x) # make sure we're dealing with a numpy array # zero pad x up to NFFT if it is shorter than NFFT @@ -297,24 +311,34 @@ x = np.resize(x, (NFFT,)) # Can't use resize method. x[n:] = 0 + if pad_to is None: + pad_to = NFFT + # for real x, ignore the negative frequencies - if np.iscomplexobj(x): numFreqs = NFFT - else: numFreqs = NFFT//2+1 + if (sides == 'default' and np.iscomplexobj(x)) or sides == 'twosided': + numFreqs = pad_to + elif sides in ('default', 'onesided'): + numFreqs = pad_to//2 + 1 + else: + raise ValueError("sides must be one of: 'default', 'onesided', or " + "twosided") if cbook.iterable(window): assert(len(window) == NFFT) windowVals = window else: - windowVals = window(np.ones((NFFT,),x.dtype)) - step = NFFT-noverlap - ind = range(0,len(x)-NFFT+1,step) + windowVals = window(np.ones((NFFT,), x.dtype)) + + step = NFFT - noverlap + ind = range(0, len(x) - NFFT + 1, step) n = len(ind) Pxx = np.zeros((numFreqs,n), np.float_) - # do the ffts of the slices + + # do the FFTs of the slices for i in range(n): thisX = x[ind[i]:ind[i]+NFFT] thisX = windowVals * detrend(thisX) - fx = np.absolute(np.fft.fft(thisX))**2 + fx = np.absolute(np.fft.fft(thisX, n=pad_to))**2 Pxx[:,i] = fx[:numFreqs] if n>1: @@ -323,7 +347,7 @@ # windowing loss; see Bendat & Piersol Sec 11.5.2 Pxx /= (np.abs(windowVals)**2).sum() - freqs = Fs/NFFT * np.arange(numFreqs) + freqs = float(Fs) / pad_to * np.arange(numFreqs) return Pxx, freqs This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 17:56:13
|
Revision: 6390 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6390&view=rev Author: ryanmay Date: 2008-11-11 17:56:07 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Update the axes.psd() method to reflect the new options available in mlab.psd. Add an example to show how the options change things. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/psd_demo2.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 16:30:03 UTC (rev 6389) +++ trunk/matplotlib/CHANGELOG 2008-11-11 17:56:07 UTC (rev 6390) @@ -1,3 +1,7 @@ +2008-11-11 Update the axes.psd() method to reflect the new options + available in mlab.psd. Add an example to show how the + options change things. - RM + 2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to allow controlling of zero padding and returning of negative frequency components, respecitively. These are Added: trunk/matplotlib/examples/pylab_examples/psd_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/psd_demo2.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/psd_demo2.py 2008-11-11 17:56:07 UTC (rev 6390) @@ -0,0 +1,39 @@ +#This example shows the effects of some of the different PSD parameters +import numpy as np +import matplotlib.pyplot as plt + +dt = np.pi / 100. +fs = 1. / dt +t = np.arange(0, 8, dt) +y = 10. * np.sin(2 * np.pi * 4 * t) + 5. * np.sin(2 * np.pi * 4.25 * t) +y = y + np.random.randn(*t.shape) + +#Plot the raw time series +fig = plt.figure() +ax = fig.add_subplot(2, 1, 1) +ax.plot(t, y) + +#Plot the PSD with different amounts of zero padding. This uses the entire +#time series at once +ax2 = fig.add_subplot(2, 3, 4) +ax2.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) +ax2.psd(y, NFFT=len(t), pad_to=len(t)*2, Fs=fs) +ax2.psd(y, NFFT=len(t), pad_to=len(t)*4, Fs=fs) +plt.title('Effect of zero padding') + +#Plot the PSD with different block sizes, Zero pad to the length of the orignal +#data sequence. +ax3 = fig.add_subplot(2, 3, 5, sharex=ax2, sharey=ax2) +ax3.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) +ax3.psd(y, NFFT=len(t)//2, pad_to=len(t), Fs=fs) +ax3.psd(y, NFFT=len(t)//4, pad_to=len(t), Fs=fs) +plt.title('Effect of block size') + +#Plot the PSD with different amounts of overlap between blocks +ax4 = fig.add_subplot(2, 3, 6, sharex=ax2, sharey=ax2) +ax4.psd(y,NFFT=len(t)//2, pad_to=len(t), noverlap=0, Fs=fs) +ax4.psd(y,NFFT=len(t)//2, pad_to=len(t), noverlap=int(0.05*len(t)/2.), Fs=fs) +ax4.psd(y,NFFT=len(t)//2, pad_to=len(t), noverlap=int(0.2*len(t)/2.), Fs=fs) +plt.title('Effect of overlap') + +plt.show() Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 16:30:03 UTC (rev 6389) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 17:56:07 UTC (rev 6390) @@ -6525,7 +6525,8 @@ hist.__doc__ = cbook.dedent(hist.__doc__) % martist.kwdocd def psd(self, x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, **kwargs): + window=mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs): """ call signature:: @@ -6595,7 +6596,8 @@ .. plot:: mpl_examples/pylab_examples/psd_demo.py """ if not self._hold: self.cla() - pxx, freqs = mlab.psd(x, NFFT, Fs, detrend, window, noverlap) + pxx, freqs = mlab.psd(x, NFFT, Fs, detrend, window, noverlap, pad_to, + sides) pxx.shape = len(freqs), freqs += Fc This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 18:42:16
|
Revision: 6392 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6392&view=rev Author: ryanmay Date: 2008-11-11 18:42:11 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Update the mlab.csd() to match the new options added to mlab.psd(). Factor out the keyword argument docs so that they can be shared between the two functions. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/mlab.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 18:39:46 UTC (rev 6391) +++ trunk/matplotlib/CHANGELOG 2008-11-11 18:42:11 UTC (rev 6392) @@ -1,3 +1,7 @@ +2008-11-11 Update the mlab.csd() to match the new options added to + mlab.psd(). Factor out the keyword argument docs so that + they can be shared between the two functions. - RM + 2008-11-11 Update the axes.psd() method to reflect the new options available in mlab.psd. Add an example to show how the options change things. - RM Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 18:39:46 UTC (rev 6391) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 18:42:11 UTC (rev 6392) @@ -252,51 +252,7 @@ *x* Array or sequence containing the data - - *NFFT* - The number of data points used in each block for the FFT. - Must be even; a power 2 is most efficient. The default value is 256. - - *Fs* - The sampling frequency (samples per time unit). It is used - to calculate the Fourier frequencies, freqs, in cycles per time - unit. The default value is 2. - - *detrend* - Any callable function (unlike in matlab where it is a vector). - For examples, see :func:`detrend`, :func:`detrend_none`, and - :func:`detrend_mean`. The default is :func:`detrend_none`. - - *window* - A function or a vector of length *NFFT*. To create window - vectors see :func:`window_hanning`, :func:`window_none`, - :func:`numpy.blackman`, :func:`numpy.hamming`, - :func:`numpy.bartlett`, :func:`scipy.signal`, - :func:`scipy.signal.get_window`, etc. The default is - :func:`window_hanning`. If a function is passed as the - argument, it must take a data segment as an argument and - return the windowed version of the segment. - - *noverlap* - The number of points of overlap between blocks. The default value - is 0 (no overlap). - - *pad_to* - The number of points to which the data segment is padd when - performing the FFT. This can be different from *NFFT*, which - specifies the number of data points used. While not increasing - the actual resolution of the psd (the minimum distance between - resolvable peaks), this can give more points in the plot, - allowing for more detail. This corresponds to the *n* parameter - in the call to fft(). The default is None, which sets *pad_to* - equal to *NFFT* - - *sides* [ 'default' | 'onesided' | 'twosided' ] - Specifies which sides of the PSD to return. Default gives the - default behavior, which returns one-sided for real data and both - for complex data. 'one' forces the return of a one-sided PSD, while - 'both' forces two-sided. - + %(PSD)s Returns the tuple (*Pxx*, *freqs*). Refs: @@ -314,14 +270,14 @@ if pad_to is None: pad_to = NFFT - # for real x, ignore the negative frequencies + # For real x, ignore the negative frequencies unless told otherwise if (sides == 'default' and np.iscomplexobj(x)) or sides == 'twosided': numFreqs = pad_to elif sides in ('default', 'onesided'): numFreqs = pad_to//2 + 1 else: raise ValueError("sides must be one of: 'default', 'onesided', or " - "twosided") + "'twosided'") if cbook.iterable(window): assert(len(window) == NFFT) @@ -351,23 +307,9 @@ return Pxx, freqs -def csd(x, y, NFFT=256, Fs=2, detrend=detrend_none, - window=window_hanning, noverlap=0): - """ - The cross power spectral density by Welch's average periodogram - method. The vectors *x* and *y* are divided into *NFFT* length - blocks. Each block is detrended by the function *detrend* and - windowed by the function *window*. *noverlap* gives the length - of the overlap between blocks. The product of the direct FFTs - of *x* and *y* are averaged over each segment to compute *Pxy*, - with a scaling to correct for power loss due to windowing. - - If len(*x*) < *NFFT* or len(*y*) < *NFFT*, they will be zero - padded to *NFFT*. - - *x*, *y* - Array or sequence containing the data - +#Split out these keyword docs so that they can be used elsewhere +kwdocd = dict() +kwdocd['PSD'] =""" *NFFT* The number of data points used in each block for the FFT. Must be even; a power 2 is most efficient. The default value is 256. @@ -388,12 +330,49 @@ :func:`numpy.blackman`, :func:`numpy.hamming`, :func:`numpy.bartlett`, :func:`scipy.signal`, :func:`scipy.signal.get_window`, etc. The default is - :func:`window_hanning`. + :func:`window_hanning`. If a function is passed as the + argument, it must take a data segment as an argument and + return the windowed version of the segment. *noverlap* The number of points of overlap between blocks. The default value is 0 (no overlap). + *pad_to* + The number of points to which the data segment is padd when + performing the FFT. This can be different from *NFFT*, which + specifies the number of data points used. While not increasing + the actual resolution of the psd (the minimum distance between + resolvable peaks), this can give more points in the plot, + allowing for more detail. This corresponds to the *n* parameter + in the call to fft(). The default is None, which sets *pad_to* + equal to *NFFT* + + *sides* [ 'default' | 'onesided' | 'twosided' ] + Specifies which sides of the PSD to return. Default gives the + default behavior, which returns one-sided for real data and both + for complex data. 'one' forces the return of a one-sided PSD, while + 'both' forces two-sided. +""" +psd.__doc__ = psd.__doc__ % kwdocd + +def csd(x, y, NFFT=256, Fs=2, detrend=detrend_none, + window=window_hanning, noverlap=0, pad_to=None, sides='default'): + """ + The cross power spectral density by Welch's average periodogram + method. The vectors *x* and *y* are divided into *NFFT* length + blocks. Each block is detrended by the function *detrend* and + windowed by the function *window*. *noverlap* gives the length + of the overlap between blocks. The product of the direct FFTs + of *x* and *y* are averaged over each segment to compute *Pxy*, + with a scaling to correct for power loss due to windowing. + + If len(*x*) < *NFFT* or len(*y*) < *NFFT*, they will be zero + padded to *NFFT*. + + *x*, *y* + Array or sequence containing the data + %(PSD)s Returns the tuple (*Pxy*, *freqs*). Refs: @@ -401,9 +380,6 @@ Procedures, John Wiley & Sons (1986) """ - if NFFT % 2: - raise ValueError, 'NFFT must be even' - x = np.asarray(x) # make sure we're dealing with a numpy array y = np.asarray(y) # make sure we're dealing with a numpy array @@ -417,40 +393,50 @@ y = np.resize(y, (NFFT,)) y[n:] = 0 - # for real x, ignore the negative frequencies - if np.iscomplexobj(x): numFreqs = NFFT - else: numFreqs = NFFT//2+1 + if pad_to is None: + pad_to = NFFT + # For real x, ignore the negative frequencies unless told otherwise + if (sides == 'default' and np.iscomplexobj(x)) or sides == 'twosided': + numFreqs = pad_to + elif sides in ('default', 'onesided'): + numFreqs = pad_to//2 + 1 + else: + raise ValueError("sides must be one of: 'default', 'onesided', or " + "'twosided'") + if cbook.iterable(window): assert(len(window) == NFFT) windowVals = window else: windowVals = window(np.ones((NFFT,), x.dtype)) - step = NFFT-noverlap - ind = range(0,len(x)-NFFT+1,step) + + step = NFFT - noverlap + ind = range(0, len(x) - NFFT + 1, step) n = len(ind) Pxy = np.zeros((numFreqs,n), np.complex_) # do the ffts of the slices for i in range(n): thisX = x[ind[i]:ind[i]+NFFT] - thisX = windowVals*detrend(thisX) + thisX = windowVals * detrend(thisX) thisY = y[ind[i]:ind[i]+NFFT] - thisY = windowVals*detrend(thisY) - fx = np.fft.fft(thisX) - fy = np.fft.fft(thisY) - Pxy[:,i] = np.conjugate(fx[:numFreqs])*fy[:numFreqs] + thisY = windowVals * detrend(thisY) + fx = np.fft.fft(thisX, n=pad_to) + fy = np.fft.fft(thisY, n=pad_to) + Pxy[:,i] = np.conjugate(fx[:numFreqs]) * fy[:numFreqs] - - # Scale the spectrum by the norm of the window to compensate for # windowing loss; see Bendat & Piersol Sec 11.5.2 if n>1: Pxy = Pxy.mean(axis=1) + Pxy /= (np.abs(windowVals)**2).sum() - freqs = Fs/NFFT*np.arange(numFreqs) + freqs = float(Fs) / pad_to * np.arange(numFreqs) return Pxy, freqs +csd.__doc__ = csd.__doc__ % kwdocd + def specgram(x, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, noverlap=128): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 19:28:41
|
Revision: 6393 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6393&view=rev Author: ryanmay Date: 2008-11-11 19:28:38 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Update the axes.csd() to match the new options added to mlab.csd(). Move some of the docs from axes.psd() to the common place in mlab. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/mlab.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 18:42:11 UTC (rev 6392) +++ trunk/matplotlib/CHANGELOG 2008-11-11 19:28:38 UTC (rev 6393) @@ -1,3 +1,7 @@ +2008-11-11 Update the axes.csd() to match the new options added to + mlab.csd(). Move some of the docs from axes.psd() to + the common place in mlab. - RM + 2008-11-11 Update the mlab.csd() to match the new options added to mlab.psd(). Factor out the keyword argument docs so that they can be shared between the two functions. - RM Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 18:42:11 UTC (rev 6392) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 19:28:38 UTC (rev 6393) @@ -6531,9 +6531,10 @@ call signature:: psd(x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, **kwargs) + window=mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs) - The power spectral density by Welches average periodogram + The power spectral density by Welch's average periodogram method. The vector *x* is divided into *NFFT* length segments. Each segment is detrended by function *detrend* and windowed by function *window*. *noverlap* gives the length of @@ -6542,41 +6543,14 @@ scaling to correct for power loss due to windowing. *Fs* is the sampling frequency. - Keyword arguments: + %(PSD)s - *NFFT*: integer - The length of the fft segment, must be a power of 2 - - *Fs*: integer - The sampling frequency. - *Fc*: integer The center frequency of *x* (defaults to 0), which offsets the x extents of the plot to reflect the frequency range used when a signal is acquired and then filtered and downsampled to baseband. - *detrend*: - The function applied to each segment before fft-ing, - designed to remove the mean or linear trend. Unlike in - matlab, where the *detrend* parameter is a vector, in - matplotlib is it a function. The :mod:`~matplotlib.pylab` - module defines :func:`~matplotlib.pylab.detrend_none`, - :func:`~matplotlib.pylab.detrend_mean`, and - :func:`~matplotlib.pylab.detrend_linear`, but you can use - a custom function as well. - - *window*: - The function used to window the segments. *window* is a - function, unlike in matlab where it is a vector. - :mod:`~matplotlib.pylab` defines - :func:`~matplotlib.pylab.window_none`, and - :func:`~matplotlib.pylab.window_hanning`, but you can use - a custom function as well. - - *noverlap*: integer - Gives the length of the overlap between segments. - Returns the tuple (*Pxx*, *freqs*). For plotting, the power is plotted as @@ -6615,17 +6589,24 @@ self.set_yticks(ticks) return pxx, freqs - psd.__doc__ = cbook.dedent(psd.__doc__) % martist.kwdocd + psd_doc_dict = dict() + psd_doc_dict.update(martist.kwdocd) + psd_doc_dict.update(mlab.kwdocd) + psd_doc_dict['PSD'] = cbook.dedent(psd_doc_dict['PSD']) + psd.__doc__ = cbook.dedent(psd.__doc__) % psd_doc_dict + def csd(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, **kwargs): + window=mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs): """ call signature:: csd(x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=window_hanning, noverlap=0, **kwargs) + window=mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs) - The cross spectral density :math:`P_{xy}` by Welches average + The cross spectral density :math:`P_{xy}` by Welch's average periodogram method. The vectors *x* and *y* are divided into *NFFT* length segments. Each segment is detrended by function *detrend* and windowed by function *window*. The product of @@ -6637,6 +6618,14 @@ (complex valued), and :math:`10\log_{10}|P_{xy}|` is plotted. + %(PSD)s + + *Fc*: integer + The center frequency of *x* (defaults to 0), which offsets + the x extents of the plot to reflect the frequency range used + when a signal is acquired and then filtered and downsampled to + baseband. + References: Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John Wiley & Sons (1986) @@ -6654,7 +6643,8 @@ For a description of the optional parameters. """ if not self._hold: self.cla() - pxy, freqs = mlab.csd(x, y, NFFT, Fs, detrend, window, noverlap) + pxy, freqs = mlab.csd(x, y, NFFT, Fs, detrend, window, noverlap, + pad_to, sides) pxy.shape = len(freqs), # pxy is complex freqs += Fc @@ -6672,7 +6662,8 @@ self.set_yticks(ticks) return pxy, freqs - csd.__doc__ = cbook.dedent(csd.__doc__) % martist.kwdocd + csd.__doc__ = cbook.dedent(csd.__doc__) % psd_doc_dict + del psd_doc_dict #So that this does not become an Axes attribute def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, **kwargs): Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 18:42:11 UTC (rev 6392) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 19:28:38 UTC (rev 6393) @@ -310,48 +310,55 @@ #Split out these keyword docs so that they can be used elsewhere kwdocd = dict() kwdocd['PSD'] =""" - *NFFT* - The number of data points used in each block for the FFT. - Must be even; a power 2 is most efficient. The default value is 256. + Keyword arguments: - *Fs* - The sampling frequency (samples per time unit). It is used - to calculate the Fourier frequencies, freqs, in cycles per time - unit. The default value is 2. + *NFFT*: integer + The number of data points used in each block for the FFT. + Must be even; a power 2 is most efficient. The default value is 256. - *detrend* - Any callable function (unlike in matlab where it is a vector). - For examples, see :func:`detrend`, :func:`detrend_none`, and - :func:`detrend_mean`. The default is :func:`detrend_none`. + *Fs*: scalar + The sampling frequency (samples per time unit). It is used + to calculate the Fourier frequencies, freqs, in cycles per time + unit. The default value is 2. - *window* - A function or a vector of length *NFFT*. To create window - vectors see :func:`window_hanning`, :func:`window_none`, - :func:`numpy.blackman`, :func:`numpy.hamming`, - :func:`numpy.bartlett`, :func:`scipy.signal`, - :func:`scipy.signal.get_window`, etc. The default is - :func:`window_hanning`. If a function is passed as the - argument, it must take a data segment as an argument and - return the windowed version of the segment. + *detrend*: callable + The function applied to each segment before fft-ing, + designed to remove the mean or linear trend. Unlike in + matlab, where the *detrend* parameter is a vector, in + matplotlib is it a function. The :mod:`~matplotlib.pylab` + module defines :func:`~matplotlib.pylab.detrend_none`, + :func:`~matplotlib.pylab.detrend_mean`, and + :func:`~matplotlib.pylab.detrend_linear`, but you can use + a custom function as well. - *noverlap* - The number of points of overlap between blocks. The default value - is 0 (no overlap). + *window*: callable or ndarray + A function or a vector of length *NFFT*. To create window + vectors see :func:`window_hanning`, :func:`window_none`, + :func:`numpy.blackman`, :func:`numpy.hamming`, + :func:`numpy.bartlett`, :func:`scipy.signal`, + :func:`scipy.signal.get_window`, etc. The default is + :func:`window_hanning`. If a function is passed as the + argument, it must take a data segment as an argument and + return the windowed version of the segment. - *pad_to* - The number of points to which the data segment is padd when - performing the FFT. This can be different from *NFFT*, which - specifies the number of data points used. While not increasing - the actual resolution of the psd (the minimum distance between - resolvable peaks), this can give more points in the plot, - allowing for more detail. This corresponds to the *n* parameter - in the call to fft(). The default is None, which sets *pad_to* - equal to *NFFT* + *noverlap*: integer + The number of points of overlap between blocks. The default value + is 0 (no overlap). - *sides* [ 'default' | 'onesided' | 'twosided' ] - Specifies which sides of the PSD to return. Default gives the - default behavior, which returns one-sided for real data and both - for complex data. 'one' forces the return of a one-sided PSD, while + *pad_to*: integer + The number of points to which the data segment is padd when + performing the FFT. This can be different from *NFFT*, which + specifies the number of data points used. While not increasing + the actual resolution of the psd (the minimum distance between + resolvable peaks), this can give more points in the plot, + allowing for more detail. This corresponds to the *n* parameter + in the call to fft(). The default is None, which sets *pad_to* + equal to *NFFT* + + *sides*: [ 'default' | 'onesided' | 'twosided' ] + Specifies which sides of the PSD to return. Default gives the + default behavior, which returns one-sided for real data and both + for complex data. 'one' forces the return of a one-sided PSD, while 'both' forces two-sided. """ psd.__doc__ = psd.__doc__ % kwdocd This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 20:34:29
|
Revision: 6395 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6395&view=rev Author: ryanmay Date: 2008-11-11 20:34:25 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Update cohere() in mlab and Axes method to match new psd() and csd() parameters. Update docs. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/mlab.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 20:20:27 UTC (rev 6394) +++ trunk/matplotlib/CHANGELOG 2008-11-11 20:34:25 UTC (rev 6395) @@ -1,15 +1,8 @@ -2008-11-11 Update the axes.csd() to match the new options added to - mlab.csd(). Move some of the docs from axes.psd() to - the common place in mlab. - RM +2008-11-11 Update the psd(), csd(), and cohere() methods of Axes + and the csd() and cohere() functions in mlab to be in + sync with the changes to psd(). In fact, under the + hood, mlab.psd() now calls mlab.csd(). - RM -2008-11-11 Update the mlab.csd() to match the new options added to - mlab.psd(). Factor out the keyword argument docs so that - they can be shared between the two functions. - RM - -2008-11-11 Update the axes.psd() method to reflect the new options - available in mlab.psd. Add an example to show how the - options change things. - RM - 2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to allow controlling of zero padding and returning of negative frequency components, respecitively. These are Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 20:20:27 UTC (rev 6394) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 20:34:25 UTC (rev 6395) @@ -6663,15 +6663,16 @@ return pxy, freqs csd.__doc__ = cbook.dedent(csd.__doc__) % psd_doc_dict - del psd_doc_dict #So that this does not become an Axes attribute def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, **kwargs): + window=mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs): """ call signature:: cohere(x, y, NFFT=256, Fs=2, Fc=0, detrend = mlab.detrend_none, - window = mlab.window_hanning, noverlap=0, **kwargs) + window = mlab.window_hanning, noverlap=0, pad_to=None, + sides='default', **kwargs) cohere the coherence between *x* and *y*. Coherence is the normalized cross spectral density: @@ -6680,6 +6681,14 @@ C_{xy} = \\frac{|P_{xy}|^2}{P_{xx}P_{yy}} + %(PSD)s + + *Fc*: integer + The center frequency of *x* (defaults to 0), which offsets + the x extents of the plot to reflect the frequency range used + when a signal is acquired and then filtered and downsampled to + baseband. + The return value is a tuple (*Cxy*, *f*), where *f* are the frequencies of the coherence vector. @@ -6698,10 +6707,6 @@ **Example:** .. plot:: mpl_examples/pylab_examples/cohere_demo.py - - .. seealso: - :meth:`psd` - For a description of the optional parameters. """ if not self._hold: self.cla() cxy, freqs = mlab.cohere(x, y, NFFT, Fs, detrend, window, noverlap) @@ -6713,7 +6718,8 @@ self.grid(True) return cxy, freqs - cohere.__doc__ = cbook.dedent(cohere.__doc__) % martist.kwdocd + cohere.__doc__ = cbook.dedent(cohere.__doc__) % psd_doc_dict + del psd_doc_dict #So that this does not become an Axes attribute def specgram(self, x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=128, Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 20:20:27 UTC (rev 6394) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-11 20:34:25 UTC (rev 6395) @@ -313,7 +313,7 @@ Specifies which sides of the PSD to return. Default gives the default behavior, which returns one-sided for real data and both for complex data. 'one' forces the return of a one-sided PSD, while - 'both' forces two-sided. + 'both' forces two-sided. """ psd.__doc__ = psd.__doc__ % kwdocd @@ -494,13 +494,11 @@ return Pxx, freqs, t - - _coh_error = """Coherence is calculated by averaging over *NFFT* length segments. Your signal is too short for your choice of *NFFT*. """ def cohere(x, y, NFFT=256, Fs=2, detrend=detrend_none, - window=window_hanning, noverlap=0): + window=window_hanning, noverlap=0, pad_to=None, sides='default'): """ The coherence between *x* and *y*. Coherence is the normalized cross spectral density: @@ -509,27 +507,30 @@ C_{xy} = \\frac{|P_{xy}|^2}{P_{xx}P_{yy}} + *x*, *y* + Array or sequence containing the data + %(PSD)s The return value is the tuple (*Cxy*, *f*), where *f* are the frequencies of the coherence vector. .. seealso:: :func:`psd` and :func:`csd`: - For information about the function arguments *NFFT*, - *detrend*, *window*, *noverlap*, as well as the methods - used to compute :math:`P_{xy}`, :math:`P_{xx}` and - :math:`P_{yy}`. + For information about the methods used to compute + :math:`P_{xy}`, :math:`P_{xx}` and :math:`P_{yy}`. """ if len(x)<2*NFFT: raise ValueError(_coh_error) - Pxx, f = psd(x, NFFT, Fs, detrend, window, noverlap) - Pyy, f = psd(y, NFFT, Fs, detrend, window, noverlap) - Pxy, f = csd(x, y, NFFT, Fs, detrend, window, noverlap) + Pxx, f = psd(x, NFFT, Fs, detrend, window, noverlap, pad_to, sides) + Pyy, f = psd(y, NFFT, Fs, detrend, window, noverlap, pad_to, sides) + Pxy, f = csd(x, y, NFFT, Fs, detrend, window, noverlap, pad_to, sides) Cxy = np.divide(np.absolute(Pxy)**2, Pxx*Pyy) Cxy.shape = (len(f),) return Cxy, f +cohere.__doc__ = cohere.__doc__ % kwdocd + def corrcoef(*args): """ corrcoef(*X*) where *X* is a matrix returns a matrix of correlation This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-11-11 21:45:19
|
Revision: 6397 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6397&view=rev Author: ryanmay Date: 2008-11-11 21:45:15 +0000 (Tue, 11 Nov 2008) Log Message: ----------- Update specgram() to use the new keyword arguments and clean up docstring. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-11 21:32:29 UTC (rev 6396) +++ trunk/matplotlib/CHANGELOG 2008-11-11 21:45:15 UTC (rev 6397) @@ -1,7 +1,8 @@ -2008-11-11 Update the psd(), csd(), and cohere() methods of Axes - and the csd() and cohere() functions in mlab to be in - sync with the changes to psd(). In fact, under the - hood, mlab.psd() now calls mlab.csd(). - RM +2008-11-11 Update the psd(), csd(), cohere(), and specgram() methods + of Axes and the csd() cohere(), and specgram() functions + in mlab to be in sync with the changes to psd(). + In fact, under the hood, these all call the same core + to do computations. - RM 2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to allow controlling of zero padding and returning of Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 21:32:29 UTC (rev 6396) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-11 21:45:15 UTC (rev 6397) @@ -6719,17 +6719,16 @@ return cxy, freqs cohere.__doc__ = cbook.dedent(cohere.__doc__) % psd_doc_dict - del psd_doc_dict #So that this does not become an Axes attribute def specgram(self, x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=128, - cmap = None, xextent=None): + cmap=None, xextent=None, pad_to=None, sides='default'): """ call signature:: specgram(x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window = mlab.window_hanning, noverlap=128, - cmap=None, xextent=None) + window=mlab.window_hanning, noverlap=128, + cmap=None, xextent=None, pad_to=None, sides='default') Compute a spectrogram of data in *x*. Data are split into *NFFT* length segments and the PSD of each section is @@ -6737,16 +6736,22 @@ segment, and the amount of overlap of each segment is specified with *noverlap*. - Keyword arguments: + %(PSD)s + *Fc*: integer + The center frequency of *x* (defaults to 0), which offsets + the y extents of the plot to reflect the frequency range used + when a signal is acquired and then filtered and downsampled to + baseband. + *cmap*: A :class:`matplotlib.cm.Colormap` instance; if *None* use default determined by rc *xextent*: - The image extent in the xaxes xextent=xmin, xmax - default 0, max(bins), 0, max(freqs) where bins is the return - value from mlab.specgram + The image extent along the x-axis. xextent = (xmin,xmax) + The default is (0,max(bins)), where bins is the return + value from :func:`mlab.specgram` Return value is (*Pxx*, *freqs*, *bins*, *im*): @@ -6757,21 +6762,21 @@ Note: If *x* is real (i.e. non-complex), only the positive spectrum is shown. If *x* is complex, both positive and - negative parts of the spectrum are shown. + negative parts of the spectrum are shown. This can be + overridden using the *sides* keyword argument. - .. seealso: - :meth:`psd` - For a description of the optional parameters. + **Example:** + + .. plot:: mpl_examples/pylab_examples/specgram_demo.py """ if not self._hold: self.cla() Pxx, freqs, bins = mlab.specgram(x, NFFT, Fs, detrend, - window, noverlap) + window, noverlap, pad_to, sides) + Z = 10. * np.log10(Pxx) + Z = np.flipud(Z) - Z = 10*np.log10(Pxx) - Z = np.flipud(Z) - if xextent is None: xextent = 0, np.amax(bins) xmin, xmax = xextent freqs += Fc @@ -6780,6 +6785,8 @@ self.axis('auto') return Pxx, freqs, bins, im + specgram.__doc__ = cbook.dedent(specgram.__doc__) % psd_doc_dict + del psd_doc_dict #So that this does not become an Axes attribute def spy(self, Z, precision=0, marker=None, markersize=None, aspect='equal', **kwargs): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-11-13 08:11:20
|
Revision: 6401 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6401&view=rev Author: efiring Date: 2008-11-13 08:11:13 +0000 (Thu, 13 Nov 2008) Log Message: ----------- Fix autoscaling problems associated with axhline etc. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/artist.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/transforms.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-12 20:09:10 UTC (rev 6400) +++ trunk/matplotlib/CHANGELOG 2008-11-13 08:11:13 UTC (rev 6401) @@ -1,3 +1,9 @@ +2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, + and use them to determine whether either or both + coordinates are used when updating dataLim. This is + used to fix autoscaling problems that had been triggered + by axhline, axhspan, axvline, axvspan. - EF + 2008-11-11 Update the psd(), csd(), cohere(), and specgram() methods of Axes and the csd() cohere(), and specgram() functions in mlab to be in sync with the changes to psd(). Modified: trunk/matplotlib/lib/matplotlib/artist.py =================================================================== --- trunk/matplotlib/lib/matplotlib/artist.py 2008-11-12 20:09:10 UTC (rev 6400) +++ trunk/matplotlib/lib/matplotlib/artist.py 2008-11-13 08:11:13 UTC (rev 6401) @@ -51,6 +51,8 @@ self.axes = None self._remove_method = None self._url = None + self.x_isdata = True # False to avoid updating Axes.dataLim with x + self.y_isdata = True # with y def remove(self): """ @@ -319,7 +321,7 @@ Returns the url """ return self._url - + def set_url(self, url): """ Sets the url for the artist Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-12 20:09:10 UTC (rev 6400) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-13 08:11:13 UTC (rev 6401) @@ -1312,8 +1312,7 @@ self._set_artist_props(line) line.set_clip_path(self.patch) - if line.get_transform() == self.transData: - self._update_line_limits(line) + self._update_line_limits(line) if not line.get_label(): line.set_label('_line%d'%len(self.lines)) self.lines.append(line) @@ -1322,7 +1321,9 @@ def _update_line_limits(self, line): p = line.get_path() if p.vertices.size > 0: - self.dataLim.update_from_path(p, self.ignore_existing_data_limits) + self.dataLim.update_from_path(p, self.ignore_existing_data_limits, + updatex=line.x_isdata, + updatey=line.y_isdata) self.ignore_existing_data_limits = False def add_patch(self, p): @@ -1356,7 +1357,8 @@ transform = (patch.get_data_transform() + self.transData.inverted()) xys = transform.transform(xys) - self.update_datalim(xys) + self.update_datalim(xys, updatex=patch.x_isdata, + updatey=patch.y_isdata) def add_table(self, tab): @@ -1381,7 +1383,7 @@ for p in self.patches: self._update_patch_limits(p) - def update_datalim(self, xys): + def update_datalim(self, xys, updatex=True, updatey=True): 'Update the data lim bbox with seq of xy tups or equiv. 2-D array' # if no data is set currently, the bbox will ignore its # limits and set the bound to be the bounds of the xydata. @@ -1391,7 +1393,8 @@ if iterable(xys) and not len(xys): return if not ma.isMaskedArray(xys): xys = np.asarray(xys) - self.dataLim.update_from_data_xy(xys, self.ignore_existing_data_limits) + self.dataLim.update_from_data_xy(xys, self.ignore_existing_data_limits, + updatex=updatex, updatey=updatey) self.ignore_existing_data_limits = False def update_datalim_numerix(self, x, y): @@ -2776,11 +2779,9 @@ trans = mtransforms.blended_transform_factory( self.transAxes, self.transData) l = mlines.Line2D([xmin,xmax], [y,y], transform=trans, **kwargs) + l.x_isdata = False self.add_line(l) - self.dataLim.y0 = min(self.dataLim.y0, yy) - self.dataLim.y1 = max(self.dataLim.y1, yy) self.autoscale_view(scalex=False, scaley=scaley) - return l axhline.__doc__ = cbook.dedent(axhline.__doc__) % martist.kwdocd @@ -2836,11 +2837,9 @@ trans = mtransforms.blended_transform_factory( self.transData, self.transAxes) l = mlines.Line2D([x,x], [ymin,ymax] , transform=trans, **kwargs) + l.y_isdata = False self.add_line(l) - self.dataLim.x0 = min(self.dataLim.x0, xx) - self.dataLim.x1 = max(self.dataLim.x1, xx) self.autoscale_view(scalex=scalex, scaley=False) - return l axvline.__doc__ = cbook.dedent(axvline.__doc__) % martist.kwdocd @@ -2858,7 +2857,7 @@ Draw a horizontal span (rectangle) from *ymin* to *ymax*. With the default values of *xmin* = 0 and *xmax* = 1, this - always span the xrange, regardless of the xlim settings, even + always spans the xrange, regardless of the xlim settings, even if you change them, eg. with the :meth:`set_xlim` command. That is, the horizontal extent is in axes coords: 0=left, 0.5=middle, 1.0=right but the *y* location is in data @@ -2896,6 +2895,7 @@ verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) + p.x_isdata = False self.add_patch(p) return p axhspan.__doc__ = cbook.dedent(axhspan.__doc__) % martist.kwdocd @@ -2913,7 +2913,7 @@ Draw a vertical span (rectangle) from *xmin* to *xmax*. With the default values of *ymin* = 0 and *ymax* = 1, this always - span the yrange, regardless of the ylim settings, even if you + spans the yrange, regardless of the ylim settings, even if you change them, eg. with the :meth:`set_ylim` command. That is, the vertical extent is in axes coords: 0=bottom, 0.5=middle, 1.0=top but the *y* location is in data coordinates. @@ -2950,6 +2950,7 @@ verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) + p.y_isdata = False self.add_patch(p) return p axvspan.__doc__ = cbook.dedent(axvspan.__doc__) % martist.kwdocd Modified: trunk/matplotlib/lib/matplotlib/transforms.py =================================================================== --- trunk/matplotlib/lib/matplotlib/transforms.py 2008-11-12 20:09:10 UTC (rev 6400) +++ trunk/matplotlib/lib/matplotlib/transforms.py 2008-11-13 08:11:13 UTC (rev 6401) @@ -776,7 +776,8 @@ def update_from_data(self, x, y, ignore=None): """ Update the bounds of the :class:`Bbox` based on the passed in - data. + data. After updating, the bounds will have positive *width* + and *height*; *x0* and *y0* will be the minimal values. *x*: a numpy array of *x*-values @@ -791,10 +792,11 @@ xy = np.hstack((x.reshape((len(x), 1)), y.reshape((len(y), 1)))) return self.update_from_data_xy(xy, ignore) - def update_from_path(self, path, ignore=None): + def update_from_path(self, path, ignore=None, updatex=True, updatey=True): """ Update the bounds of the :class:`Bbox` based on the passed in - data. + data. After updating, the bounds will have positive *width* + and *height*; *x0* and *y0* will be the minimal values. *path*: a :class:`~matplotlib.path.Path` instance @@ -802,6 +804,10 @@ - when True, ignore the existing bounds of the :class:`Bbox`. - when False, include the existing bounds of the :class:`Bbox`. - when None, use the last value passed to :meth:`ignore`. + + *updatex*: when True, update the x values + + *updatey*: when True, update the y values """ if ignore is None: ignore = self._ignore @@ -813,15 +819,20 @@ path, None, self._points, self._minpos, ignore) if changed: - self._points = points - self._minpos = minpos self.invalidate() + if updatex: + self._points[:,0] = points[:,0] + self._minpos[0] = minpos[0] + if updatey: + self._points[:,1] = points[:,1] + self._minpos[1] = minpos[1] - def update_from_data_xy(self, xy, ignore=None): + def update_from_data_xy(self, xy, ignore=None, updatex=True, updatey=True): """ Update the bounds of the :class:`Bbox` based on the passed in - data. + data. After updating, the bounds will have positive *width* + and *height*; *x0* and *y0* will be the minimal values. *xy*: a numpy array of 2D points @@ -829,12 +840,17 @@ - when True, ignore the existing bounds of the :class:`Bbox`. - when False, include the existing bounds of the :class:`Bbox`. - when None, use the last value passed to :meth:`ignore`. + + *updatex*: when True, update the x values + + *updatey*: when True, update the y values """ if len(xy) == 0: return path = Path(xy) - self.update_from_path(path, ignore=ignore) + self.update_from_path(path, ignore=ignore, + updatex=updatex, updatey=updatey) def _set_x0(self, val): self._points[0, 0] = val This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pki...@us...> - 2008-11-14 22:19:06
|
Revision: 6404 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6404&view=rev Author: pkienzle Date: 2008-11-14 22:19:02 +0000 (Fri, 14 Nov 2008) Log Message: ----------- patches: allow partial rings with Wedge Modified Paths: -------------- trunk/matplotlib/examples/api/patch_collection.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/examples/api/patch_collection.py =================================================================== --- trunk/matplotlib/examples/api/patch_collection.py 2008-11-14 15:33:32 UTC (rev 6403) +++ trunk/matplotlib/examples/api/patch_collection.py 2008-11-14 22:19:02 UTC (rev 6404) @@ -25,12 +25,20 @@ wedge = Wedge((x1,y1), r, t1, t2) patches.append(wedge) +# Some limiting conditions on Wedge +patches += [ + Wedge((.3,.7), .1, 0, 360), # Full circle + Wedge((.7,.8), .2, 0, 360, width=0.05), # Full ring + Wedge((.8,.3), .2, 0, 45), # Full sector + Wedge((.8,.3), .2, 45, 90, width=0.10), # Ring sector +] + for i in range(N): polygon = Polygon(pylab.rand(N,2), True) patches.append(polygon) colors = 100*pylab.rand(len(patches)) -p = PatchCollection(patches, cmap=matplotlib.cm.jet) +p = PatchCollection(patches, cmap=matplotlib.cm.jet, alpha=0.4) p.set_array(pylab.array(colors)) ax.add_collection(p) pylab.colorbar(p) Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-11-14 15:33:32 UTC (rev 6403) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-11-14 22:19:02 UTC (rev 6404) @@ -712,13 +712,18 @@ :meth:`~matplotlib.patches.Polygon.set_xy` instead.""") class Wedge(Patch): + """ + Wedge shaped patch. + """ def __str__(self): return "Wedge(%g,%g)"%(self.theta1,self.theta2) - def __init__(self, center, r, theta1, theta2, **kwargs): + def __init__(self, center, r, theta1, theta2, width=0, **kwargs): """ Draw a wedge centered at *x*, *y* center with radius *r* that - sweeps *theta1* to *theta2* (in degrees). + sweeps *theta1* to *theta2* (in degrees). If *width* is given, + then a partial wedge is drawn from inner radius *r* - *width* + to outer radius *r*. Valid kwargs are: @@ -726,24 +731,43 @@ """ Patch.__init__(self, **kwargs) self.center = center - self.r = r - self.theta1 = theta1 - self.theta2 = theta2 + self.r,self.width = r,width + self.theta1,self.theta2 = theta1,theta2 + + # Inner and outer rings are connected unless the annulus is complete + delta=theta2-theta1 + if abs((theta2-theta1) - 360) <= 1e-12: + theta1,theta2 = 0,360 + connector = Path.MOVETO + else: + connector = Path.LINETO + + # Form the outer ring + arc = Path.arc(theta1,theta2) + + if width != 0: + # Partial annulus needs to draw the outter ring + # followed by a reversed and scaled inner ring + v1 = arc.vertices + v2 = arc.vertices[::-1]*float(r-width)/r + v = np.vstack([v1,v2,v1[0,:],(0,0)]) + c = np.hstack([arc.codes,arc.codes,connector,Path.CLOSEPOLY]) + c[len(arc.codes)]=connector + else: + # Wedge doesn't need an inner ring + v = np.vstack([arc.vertices,[(0,0),arc.vertices[0,:],(0,0)]]) + c = np.hstack([arc.codes,[connector,connector,Path.CLOSEPOLY]]) + + # Shift and scale the wedge to the final location. + v *= r + v += np.asarray(center) + self._path = Path(v,c) self._patch_transform = transforms.IdentityTransform() - self._path = Path.wedge(self.theta1, self.theta2) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_path(self): return self._path - def get_patch_transform(self): - x = self.convert_xunits(self.center[0]) - y = self.convert_yunits(self.center[1]) - rx = self.convert_xunits(self.r) - ry = self.convert_yunits(self.r) - self._patch_transform = transforms.Affine2D() \ - .scale(rx, ry).translate(x, y) - return self._patch_transform # COVERAGE NOTE: Not used internally or from examples class Arrow(Patch): @@ -3042,4 +3066,3 @@ self.fill = fill_orig #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-11-17 14:37:26
|
Revision: 6406 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6406&view=rev Author: mdboom Date: 2008-11-17 14:37:14 +0000 (Mon, 17 Nov 2008) Log Message: ----------- First pass formatting dates.py documentation. Modified Paths: -------------- trunk/matplotlib/doc/api/index.rst trunk/matplotlib/lib/matplotlib/dates.py Added Paths: ----------- trunk/matplotlib/doc/api/dates_api.rst Added: trunk/matplotlib/doc/api/dates_api.rst =================================================================== --- trunk/matplotlib/doc/api/dates_api.rst (rev 0) +++ trunk/matplotlib/doc/api/dates_api.rst 2008-11-17 14:37:14 UTC (rev 6406) @@ -0,0 +1,14 @@ +**************** +matplotlib dates +**************** + +.. inheritance-diagram:: matplotlib.dates + :parts: 1 + +:mod:`matplotlib.dates` +======================= + +.. automodule:: matplotlib.dates + :members: + :undoc-members: + :show-inheritance: Modified: trunk/matplotlib/doc/api/index.rst =================================================================== --- trunk/matplotlib/doc/api/index.rst 2008-11-14 22:25:28 UTC (rev 6405) +++ trunk/matplotlib/doc/api/index.rst 2008-11-17 14:37:14 UTC (rev 6406) @@ -21,6 +21,7 @@ collections_api.rst colorbar_api.rst colors_api.rst + dates_api.rst figure_api.rst font_manager_api.rst nxutils_api.rst Modified: trunk/matplotlib/lib/matplotlib/dates.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dates.py 2008-11-14 22:25:28 UTC (rev 6405) +++ trunk/matplotlib/lib/matplotlib/dates.py 2008-11-17 14:37:14 UTC (rev 6406) @@ -1,35 +1,37 @@ #!/usr/bin/env python """ - Matplotlib provides sophisticated date plotting capabilities, standing -on the shoulders of python datetime, the add-on modules pytz and -dateutils. datetime objects are converted to floating point numbers -which represent the number of days since 0001-01-01 UTC. The helper -functions date2num, num2date and drange are used to facilitate easy -conversion to and from datetime and numeric ranges. +on the shoulders of python :mod:`datetime`, the add-on modules +:mod:`pytz` and :mod:`dateutils`. :class:`datetime` objects are +converted to floating point numbers which represent the number of days +since 0001-01-01 UTC. The helper functions :func:`date2num`, +:func:`num2date` and :func:`drange` are used to facilitate easy +conversion to and from :mod:`datetime` and numeric ranges. A wide range of specific and general purpose date tick locators and formatters are provided in this module. See -:module:`matplotlib.ticker` for general information on tick locators +:mod:`matplotlib.ticker` for general information on tick locators and formatters. These are described below. All the matplotlib date converters, tickers and formatters are timezone aware, and the default timezone is given by the timezone -parameter in your matplotlibrc file. If you leave out a tz timezone -instance, the default from your rc file will be assumed. If you want -to use a custom time zone, pass a pytz.timezone instance -with the tz keyword argument to num2date, plot_date, and any custom -date tickers or locators you create. See http://pytz.sourceforge.net -for information on pytz and timezone handling. +parameter in your :file:`matplotlibrc` file. If you leave out a +:class:`tz` timezone instance, the default from your rc file will be +assumed. If you want to use a custom time zone, pass a +:class:`pytz.timezone` instance with the tz keyword argument to +:func:`num2date`, :func:`plot_date`, and any custom date tickers or +locators you create. See `pytz <http://pytz.sourceforge.net>`_ for +information on :mod:`pytz` and timezone handling. -The dateutil module (http://labix.org/python-dateutil) -provides additional code to handle -date ticking, making it easy to place ticks on any kinds of dates - -see examples below. +The `dateutil module <http://labix.org/python-dateutil>`_ provides +additional code to handle date ticking, making it easy to place ticks +on any kinds of dates. See examples below. -Date tickers - +Date tickers +------------ - Most of the date tickers can locate single or multiple values. Eg +Most of the date tickers can locate single or multiple values. For +example:: # tick on mondays every week loc = WeekdayLocator(byweekday=MO, tz=tz) @@ -37,44 +39,48 @@ # tick on mondays and saturdays loc = WeekdayLocator(byweekday=(MO, SA)) - In addition, most of the constructors take an interval argument. +In addition, most of the constructors take an interval argument:: # tick on mondays every second week loc = WeekdayLocator(byweekday=MO, interval=2) - The rrule locator allows completely general date ticking +The rrule locator allows completely general date ticking:: # tick every 5th easter rule = rrulewrapper(YEARLY, byeaster=1, interval=5) loc = RRuleLocator(rule) - Here are all the date tickers +Here are all the date tickers: - * MinuteLocator - locate minutes + * :class:`MinuteLocator`: locate minutes - * HourLocator - locate hours + * :class:`HourLocator`: locate hours - * DayLocator - locate specifed days of the month + * :class:`DayLocator`: locate specifed days of the month - * WeekdayLocator - Locate days of the week, eg MO, TU + * :class:`WeekdayLocator`: Locate days of the week, eg MO, TU - * MonthLocator - locate months, eg 7 for july + * :class:`MonthLocator`: locate months, eg 7 for july - * YearLocator - locate years that are multiples of base + * :class:`YearLocator`: locate years that are multiples of base - * RRuleLocator - locate using a matplotlib.dates.rrulewrapper. - The rrulewrapper is a simple wrapper around a dateutils.rrule - https://moin.conectiva.com.br/DateUtil which allow almost - arbitrary date tick specifications. See - examples/date_demo_rrule.py + * :class:`RRuleLocator`: locate using a + :class:`matplotlib.dates.rrulewrapper`. The + :class:`rrulewrapper` is a simple wrapper around a + :class:`dateutils.rrule` (`dateutil + <https://moin.conectiva.com.br/DateUtil>`_) which allow almost + arbitrary date tick specifications. See `rrule example + <../examples/pylab_examples/date_demo_rrule.html>`_. - Date formatters +--------------- - DateFormatter - use strftime format strings +Here all all the date formatters: - DateIndexFormatter - date plots with implicit x indexing. + * :class:`DateFormatter`: use :func:`strftime` format strings + * :class:`IndexDateFormatter`: date plots with implicit *x* + indexing. """ import re, time, math, datetime @@ -137,9 +143,9 @@ def _to_ordinalf(dt): """ - convert datetime to the Gregorian date as UTC float days, - preserving hours, minutes, seconds and microseconds. return value - is a float + Convert :mod:`datetime` to the Gregorian date as UTC float days, + preserving hours, minutes, seconds and microseconds. Return value + is a :func:`float`. """ if hasattr(dt, 'tzinfo') and dt.tzinfo is not None: @@ -156,8 +162,8 @@ def _from_ordinalf(x, tz=None): """ - convert Gregorian float of the date, preserving hours, minutes, - seconds and microseconds. return value is a datetime + Convert Gregorian float of the date, preserving hours, minutes, + seconds and microseconds. Return value is a :class:`datetime`. """ if tz is None: tz = _get_rc_timezone() ix = int(x) @@ -181,7 +187,7 @@ """ Use this class to parse date strings to matplotlib datenums when you know the date format string of the date you are parsing. See - examples/load_demo.py + :file:`examples/load_demo.py`. """ def __init__(self, fmt): """ fmt: any valid strptime format is supported """ @@ -195,8 +201,9 @@ def datestr2num(d): """ - Convert a date string to a datenum using dateutil.parser.parse - d can be a single string or a sequence of strings + Convert a date string to a datenum using + :func:`dateutil.parser.parse`. *d* can be a single string or a + sequence of strings. """ if cbook.is_string_like(d): dt = dateutil.parser.parse(d) @@ -207,35 +214,36 @@ def date2num(d): """ - d is either a datetime instance or a sequence of datetimes + *d* is either a :class:`datetime` instance or a sequence of datetimes. - return value is a floating point number (or sequence of floats) + Return value is a floating point number (or sequence of floats) which gives number of days (fraction part represents hours, - minutes, seconds) since 0001-01-01 00:00:00 UTC + minutes, seconds) since 0001-01-01 00:00:00 UTC. """ if not cbook.iterable(d): return _to_ordinalf(d) else: return np.asarray([_to_ordinalf(val) for val in d]) def julian2num(j): - 'convert a Julian date (or sequence) to a matplotlib date (or sequence)' + 'Convert a Julian date (or sequence) to a matplotlib date (or sequence).' if cbook.iterable(j): j = np.asarray(j) return j + 1721425.5 def num2julian(n): - 'convert a matplotlib date (or seguence) to a Julian date (or sequence)' + 'Convert a matplotlib date (or sequence) to a Julian date (or sequence).' if cbook.iterable(n): n = np.asarray(n) return n - 1721425.5 def num2date(x, tz=None): """ - x is a float value which gives number of days (fraction part - represents hours, minutes, seconds) since 0001-01-01 00:00:00 UTC + *x* is a float value which gives number of days (fraction part + represents hours, minutes, seconds) since 0001-01-01 00:00:00 UTC. - Return value is a datetime instance in timezone tz (default to - rcparams TZ value) + Return value is a :class:`datetime` instance in timezone *tz* (default to + rcparams TZ value). - if x is a sequence, a sequence of datetimes will be returned + If *x* is a sequence, a sequence of :class:`datetime` objects will + be returned. """ if tz is None: tz = _get_rc_timezone() if not cbook.iterable(x): return _from_ordinalf(x, tz) @@ -243,8 +251,9 @@ def drange(dstart, dend, delta): """ - Return a date range as float gregorian ordinals. dstart and dend - are datetime instances. delta is a datetime.timedelta instance + Return a date range as float Gregorian ordinals. *dstart* and + *dend* are :class:`datetime` instances. *delta* is a + :class:`datetime.timedelta` instance. """ step = (delta.days + delta.seconds/SECONDS_PER_DAY + delta.microseconds/MUSECONDS_PER_DAY) @@ -260,20 +269,21 @@ class DateFormatter(ticker.Formatter): """ - Tick location is seconds since the epoch. Use a strftime format - string + Tick location is seconds since the epoch. Use a :func:`strftime` + format string. - python only supports datetime strftime formatting for years - greater than 1900. Thanks to Andrew Dalke, Dalke Scientific - Software who contributed the strftime code below to include dates - earlier than this year + Python only supports :mod:`datetime` :func:`strftime` formatting + for years greater than 1900. Thanks to Andrew Dalke, Dalke + Scientific Software who contributed the :func:`strftime` code + below to include dates earlier than this year. """ illegal_s = re.compile(r"((^|[^%])(%%)*%s)") def __init__(self, fmt, tz=None): """ - fmt is an strftime format string; tz is the tzinfo instance + *fmt* is an :func:`strftime` format string; *tz* is the + :class:`tzinfo` instance. """ if tz is None: tz = _get_rc_timezone() self.fmt = fmt @@ -341,13 +351,13 @@ class IndexDateFormatter(ticker.Formatter): """ - Use with IndexLocator to cycle format strings by index. + Use with :class:`~matplotlib.ticker.IndexLocator` to cycle format + strings by index. """ def __init__(self, t, fmt, tz=None): """ - t is a sequence of dates floating point days). fmt is a - strftime format string - + *t* is a sequence of dates (floating point days). *fmt* is a + :func:`strftime` format string. """ if tz is None: tz = _get_rc_timezone() self.t = t @@ -355,7 +365,7 @@ self.tz = tz def __call__(self, x, pos=0): - 'Return the label for time x at position pos' + 'Return the label for time *x* at position *pos*' ind = int(round(x)) if ind>=len(self.t) or ind<=0: return '' @@ -366,8 +376,8 @@ class AutoDateFormatter(ticker.Formatter): """ - This class attempt to figure out the best format to use. This is - most useful when used with the AutoDateLocator. + This class attempts to figure out the best format to use. This is + most useful when used with the :class:`AutoDateLocator`. """ # This can be improved by providing some user-level direction on @@ -427,7 +437,7 @@ hms0d = {'byhour':0, 'byminute':0,'bysecond':0} def __init__(self, tz=None): """ - tz is the tzinfo instance + *tz* is a :class:`tzinfo` instance. """ if tz is None: tz = _get_rc_timezone() self.tz = tz @@ -445,8 +455,8 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 1 @@ -477,8 +487,8 @@ def _get_unit(self): """ - Return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ freq = self.rule._rrule._freq if ( freq == YEARLY ): @@ -501,7 +511,7 @@ def autoscale(self): """ - Set the view limits to include the data range + Set the view limits to include the data range. """ dmin, dmax = self.datalim_to_dt() if dmin>dmax: @@ -526,8 +536,9 @@ class AutoDateLocator(DateLocator): """ - On autoscale this class picks the best MultipleDateLocator to set the - view limits and the tick locs. + On autoscale, this class picks the best + :class:`MultipleDateLocator` to set the view limits and the tick + locations. """ def __init__(self, tz=None): DateLocator.__init__(self, tz) @@ -544,7 +555,7 @@ self._locator.set_axis(axis) def refresh(self): - 'refresh internal information based on current lim' + 'Refresh internal information based on current limits.' dmin, dmax = self.viewlim_to_dt() self._locator = self.get_locator(dmin, dmax) @@ -568,13 +579,13 @@ return -1 def autoscale(self): - 'Try to choose the view limits intelligently' + 'Try to choose the view limits intelligently.' dmin, dmax = self.datalim_to_dt() self._locator = self.get_locator(dmin, dmax) return self._locator.autoscale() def get_locator(self, dmin, dmax): - 'pick the best locator based on a distance' + 'Pick the best locator based on a distance.' delta = relativedelta(dmax, dmin) @@ -682,19 +693,18 @@ """ Make ticks on a given day of each year that is a multiple of base. - Examples: - # Tick every year on Jan 1st - locator = YearLocator() + Examples:: - # Tick every 5 years on July 4th - locator = YearLocator(5, month=7, day=4) + # Tick every year on Jan 1st + locator = YearLocator() - + # Tick every 5 years on July 4th + locator = YearLocator(5, month=7, day=4) """ def __init__(self, base=1, month=1, day=1, tz=None): """ - mark years that are multiple of base on a given month and day - (default jan 1) + Mark years that are multiple of base on a given month and day + (default jan 1). """ DateLocator.__init__(self, tz) self.base = ticker.Base(base) @@ -709,8 +719,8 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 365 @@ -729,7 +739,7 @@ def autoscale(self): """ - Set the view limits to include the data range + Set the view limits to include the data range. """ dmin, dmax = self.datalim_to_dt() @@ -744,15 +754,15 @@ class MonthLocator(RRuleLocator): """ - Make ticks on occurances of each month month, eg 1, 3, 12 + Make ticks on occurances of each month month, eg 1, 3, 12. """ def __init__(self, bymonth=None, bymonthday=1, interval=1, tz=None): """ - mark every month in bymonth; bymonth can be an int or - sequence. default is range(1,13), ie every month + Mark every month in *bymonth*; *bymonth* can be an int or + sequence. Default is ``range(1,13)``, i.e. every month. - interval is the interval between each iteration. Eg, if - interval=2, mark every second occurance + *interval* is the interval between each iteration. For + example, if ``interval=2``, mark every second occurance. """ if bymonth is None: bymonth=range(1,13) o = rrulewrapper(MONTHLY, bymonth=bymonth, bymonthday=bymonthday, @@ -761,28 +771,27 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 30 class WeekdayLocator(RRuleLocator): """ - Make ticks on occurances of each weekday + Make ticks on occurances of each weekday. """ def __init__(self, byweekday=1, interval=1, tz=None): """ - mark every weekday in byweekday; byweekday can be a number or - sequence + Mark every weekday in *byweekday*; *byweekday* can be a number or + sequence. - elements of byweekday must be one of MO, TU, WE, TH, FR, SA, - SU, the constants from dateutils.rrule + Elements of *byweekday* must be one of MO, TU, WE, TH, FR, SA, + SU, the constants from :mod:`dateutils.rrule`. - interval specifies the number of weeks to skip. Ie interval=2 - plots every second week - + *interval* specifies the number of weeks to skip. For example, + ``interval=2`` plots every second week. """ o = rrulewrapper(DAILY, byweekday=byweekday, interval=interval, **self.hms0d) @@ -790,21 +799,23 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 7 class DayLocator(RRuleLocator): """ - Make ticks on occurances of each day of the month, eg 1, 15, 30 + Make ticks on occurances of each day of the month. For example, + 1, 15, 30. """ def __init__(self, bymonthday=None, interval=1, tz=None): """ - mark every day in bymonthday; bymonthday can be an int or sequence + Mark every day in *bymonthday*; *bymonthday* can be an int or + sequence. - Default is to tick every day of the month - bymonthday=range(1,32) + Default is to tick every day of the month: ``bymonthday=range(1,32)`` """ if bymonthday is None: bymonthday=range(1,32) o = rrulewrapper(DAILY, bymonthday=bymonthday, @@ -813,22 +824,22 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 1 class HourLocator(RRuleLocator): """ - Make ticks on occurances of each hour + Make ticks on occurances of each hour. """ def __init__(self, byhour=None, interval=1, tz=None): """ - mark every hour in byhour; byhour can be an int or sequence. - Default is to tick every hour - byhour=range(24) + Mark every hour in *byhour*; *byhour* can be an int or sequence. + Default is to tick every hour: ``byhour=range(24)`` - interval is the interval between each iteration. Eg, if - interval=2, mark every second occurance + *interval* is the interval between each iteration. For + example, if ``interval=2``, mark every second occurrence. """ if byhour is None: byhour=range(24) rule = rrulewrapper(HOURLY, byhour=byhour, interval=interval, @@ -844,16 +855,15 @@ class MinuteLocator(RRuleLocator): """ - Make ticks on occurances of each minute + Make ticks on occurances of each minute. """ def __init__(self, byminute=None, interval=1, tz=None): """ - mark every minute in byminute; byminute can be an int or - sequence. default is to tick every minute - byminute=range(60) + Mark every minute in *byminute*; *byminute* can be an int or + sequence. Default is to tick every minute: ``byminute=range(60)`` - interval is the interval between each iteration. Eg, if - interval=2, mark every second occurance - + *interval* is the interval between each iteration. For + example, if ``interval=2``, mark every second occurrence. """ if byminute is None: byminute=range(60) rule = rrulewrapper(MINUTELY, byminute=byminute, interval=interval, @@ -862,22 +872,22 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 1./(24*60) class SecondLocator(RRuleLocator): """ - Make ticks on occurances of each second + Make ticks on occurances of each second. """ def __init__(self, bysecond=None, interval=1, tz=None): """ - mark every second in bysecond; bysecond can be an int or - sequence. Default is to tick every second bysecond = range(60) + Mark every second in *bysecond*; *bysecond* can be an int or + sequence. Default is to tick every second: ``bysecond = range(60)`` - interval is the interval between each iteration. Eg, if - interval=2, mark every second occurance + *interval* is the interval between each iteration. For + example, if ``interval=2``, mark every second occurrence. """ if bysecond is None: bysecond=range(60) @@ -886,44 +896,44 @@ def _get_unit(self): """ - return how many days a unit of the locator is; use for - intelligent autoscaling + Return how many days a unit of the locator is; used for + intelligent autoscaling. """ return 1./(24*60*60) def _close_to_dt(d1, d2, epsilon=5): - 'assert that datetimes d1 and d2 are within epsilon microseconds' + 'Assert that datetimes *d1* and *d2* are within *epsilon* microseconds.' delta = d2-d1 mus = abs(delta.days*MUSECONDS_PER_DAY + delta.seconds*1e6 + delta.microseconds) assert(mus<epsilon) def _close_to_num(o1, o2, epsilon=5): - 'assert that float ordinals o1 and o2 are within epsilon microseconds' + 'Assert that float ordinals *o1* and *o2* are within *epsilon* microseconds.' delta = abs((o2-o1)*MUSECONDS_PER_DAY) assert(delta<epsilon) def epoch2num(e): """ - convert an epoch or sequence of epochs to the new date format, - days since 0001 + Convert an epoch or sequence of epochs to the new date format, + that is days since 0001. """ spd = 24.*3600. return 719163 + np.asarray(e)/spd def num2epoch(d): """ - convert days since 0001 to epoch. d can be a number or sequence + Convert days since 0001 to epoch. *d* can be a number or sequence. """ spd = 24.*3600. return (np.asarray(d)-719163)*spd def mx2num(mxdates): """ - Convert mx datetime instance (or sequence of mx instances) to the - new date format, + Convert mx :class:`datetime` instance (or sequence of mx + instances) to the new date format. """ scalar = False if not cbook.iterable(mxdates): @@ -936,10 +946,8 @@ def date_ticker_factory(span, tz=None, numticks=5): """ - Create a date locator with numticks (approx) and a date formatter - for span in days. Return value is (locator, formatter) - - + Create a date locator with *numticks* (approx) and a date formatter + for *span* in days. Return value is (locator, formatter). """ if span==0: span = 1/24. @@ -979,19 +987,19 @@ def seconds(s): - 'return seconds as days' + 'Return seconds as days.' return float(s)/SEC_PER_DAY def minutes(m): - 'return minutes as days' + 'Return minutes as days.' return float(m)/MINUTES_PER_DAY def hours(h): - 'return hours as days' + 'Return hours as days.' return h/24. def weeks(w): - 'return weeks as days' + 'Return weeks as days.' return w*7. @@ -1016,7 +1024,7 @@ convert = staticmethod(convert) def default_units(x): - 'return the default unit for x or None' + 'Return the default unit for *x* or None' return 'date' default_units = staticmethod(default_units) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |