From: <ef...@us...> - 2008-11-18 23:38:59
|
Revision: 6414 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6414&view=rev Author: efiring Date: 2008-11-18 23:38:53 +0000 (Tue, 18 Nov 2008) Log Message: ----------- New custom colormap example; and fix typo in Axes.autoscale_view Modified Paths: -------------- trunk/matplotlib/examples/tests/backend_driver.py trunk/matplotlib/lib/matplotlib/axes.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/custom_cmap.py Added: trunk/matplotlib/examples/pylab_examples/custom_cmap.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/custom_cmap.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/custom_cmap.py 2008-11-18 23:38:53 UTC (rev 6414) @@ -0,0 +1,134 @@ +#!/usr/bin/env python + +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.colors import LinearSegmentedColormap + +""" + +Example: suppose you want red to increase from 0 to 1 over the bottom +half, green to do the same over the middle half, and blue over the top +half. Then you would use: + +cdict = {'red': ((0.0, 0.0, 0.0), + (0.5, 1.0, 1.0), + (1.0, 1.0, 1.0)), + + 'green': ((0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0)), + + 'blue': ((0.0, 0.0, 0.0), + (0.5, 0.0, 0.0), + (1.0, 1.0, 1.0))} + +If, as in this example, there are no discontinuities in the r, g, and b +components, then it is quite simple: the second and third element of +each tuple, above, is the same--call it "y". The first element ("x") +defines interpolation intervals over the full range of 0 to 1, and it +must span that whole range. In other words, the values of x divide the +0-to-1 range into a set of segments, and y gives the end-point color +values for each segment. + +Now consider the green. cdict['green'] is saying that for +0 <= x <= 0.25, y is zero; no green. +0.25 < x <= 0.75, y varies linearly from 0 to 1. +x > 0.75, y remains at 1, full green. + +If there are discontinuities, then it is a little more complicated. +Label the 3 elements in each row in the cdict entry for a given color as +(x, y0, y1). Then for values of x between x[i] and x[i+1] the color +value is interpolated between y1[i] and y0[i+1]. + +Going back to the cookbook example, look at cdict['red']; because y0 != +y1, it is saying that for x from 0 to 0.5, red increases from 0 to 1, +but then it jumps down, so that for x from 0.5 to 1, red increases from +0.7 to 1. Green ramps from 0 to 1 as x goes from 0 to 0.5, then jumps +back to 0, and ramps back to 1 as x goes from 0.5 to 1. + +row i: x y0 y1 + / + / +row i+1: x y0 y1 + +Above is an attempt to show that for x in the range x[i] to x[i+1], the +interpolation is between y1[i] and y0[i+1]. So, y0[0] and y1[-1] are +never used. + +""" + + + +cdict1 = {'red': ((0.0, 0.0, 0.0), + (0.5, 0.0, 0.1), + (1.0, 1.0, 1.0)), + + 'green': ((0.0, 0.0, 0.0), + (1.0, 0.0, 0.0)), + + 'blue': ((0.0, 0.0, 1.0), + (0.5, 0.1, 0.0), + (1.0, 0.0, 0.0)) + } + +cdict2 = {'red': ((0.0, 0.0, 0.0), + (0.5, 0.0, 1.0), + (1.0, 0.1, 1.0)), + + 'green': ((0.0, 0.0, 0.0), + (1.0, 0.0, 0.0)), + + 'blue': ((0.0, 0.0, 0.1), + (0.5, 1.0, 0.0), + (1.0, 0.0, 0.0)) + } + +cdict3 = {'red': ((0.0, 0.0, 0.0), + (0.25,0.0, 0.0), + (0.5, 0.8, 1.0), + (0.75,1.0, 1.0), + (1.0, 0.4, 1.0)), + + 'green': ((0.0, 0.0, 0.0), + (0.25,0.0, 0.0), + (0.5, 0.9, 0.9), + (0.75,0.0, 0.0), + (1.0, 0.0, 0.0)), + + 'blue': ((0.0, 0.0, 0.4), + (0.25,1.0, 1.0), + (0.5, 1.0, 0.8), + (0.75,0.0, 0.0), + (1.0, 0.0, 0.0)) + } + + +blue_red1 = LinearSegmentedColormap('BlueRed1', cdict1) +blue_red2 = LinearSegmentedColormap('BlueRed2', cdict2) +blue_red3 = LinearSegmentedColormap('BlueRed3', cdict3) + +x = np.arange(0, np.pi, 0.1) +y = np.arange(0, 2*np.pi, 0.1) +X, Y = np.meshgrid(x,y) +Z = np.cos(X) * np.sin(Y) + +plt.figure(figsize=(10,4)) +plt.subplots_adjust(wspace=0.3) + +plt.subplot(1,3,1) +plt.imshow(Z, interpolation='nearest', cmap=blue_red1) +plt.colorbar() + +plt.subplot(1,3,2) +plt.imshow(Z, interpolation='nearest', cmap=blue_red2) +plt.colorbar() + +plt.subplot(1,3,3) +plt.imshow(Z, interpolation='nearest', cmap=blue_red3) +plt.colorbar() + +plt.suptitle('Custom Blue-Red colormaps') + +plt.show() + Modified: trunk/matplotlib/examples/tests/backend_driver.py =================================================================== --- trunk/matplotlib/examples/tests/backend_driver.py 2008-11-18 21:37:25 UTC (rev 6413) +++ trunk/matplotlib/examples/tests/backend_driver.py 2008-11-18 23:38:53 UTC (rev 6414) @@ -43,6 +43,7 @@ 'contour_demo.py', 'contour_label_demo.py', 'contourf_demo.py', + 'custom_cmap.py', 'geo_demo.py', 'griddata_demo.py', 'csd_demo.py', Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-18 21:37:25 UTC (rev 6413) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-18 23:38:53 UTC (rev 6414) @@ -1496,7 +1496,7 @@ if scalex: self.set_xbound(x0, x1) if scaley: - self.set_ybound(y0, 11) + self.set_ybound(y0, y1) return if scalex: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-11-19 14:45:51
|
Revision: 6415 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6415&view=rev Author: mdboom Date: 2008-11-19 14:45:44 +0000 (Wed, 19 Nov 2008) Log Message: ----------- Converted API_CHANGES to reST and included it in the documentation tree. Modified Paths: -------------- trunk/matplotlib/doc/api/index.rst Added Paths: ----------- trunk/matplotlib/doc/api/api_changes.rst Removed Paths: ------------- trunk/matplotlib/API_CHANGES Deleted: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2008-11-18 23:38:53 UTC (rev 6414) +++ trunk/matplotlib/API_CHANGES 2008-11-19 14:45:44 UTC (rev 6415) @@ -1,1310 +0,0 @@ -Changes for 0.98.x -================== - -* set_xlim, ylim now return a copy of the viewlim array to - avoid modify inplace surprises - -* AFM.get_fullname() and get_familyname() no longer raise an - exception if the AFM file does not specify these optional attributes, - but returns a guess based on the required FontName attribute. - -* Changed precision kwarg in spy; default is 0, and the string value - 'present' is used for sparse arrays only to show filled locations. - -* EllipseCollection added. - -* Added angles kwarg to quiver for more flexible specification of - the arrow angles. - -* Deprecated (raise NotImplementedError) all the mlab2 functions from - matplotlib.mlab out of concern that some of them were not clean room - implementations. - -* Methods get_offsets and set_offsets added to Collections base - class. - -* Figure.figurePatch renamed Figure.patch, Axes.axesPatch renamed - Axes.patch, Axes.axesFrame renamed Axes.frame, Axes.get_frame, which - returns Axes.patch, is deprecated. Examples and users guide updated - -* Changes in the ContourLabeler attributes (clabel function) so that they - all have a form like .labelAttribute. The three attributes that are most - likely to be used by end users, .cl, .cl_xy and .cl_cvalues have been - maintained for the moment (in addition to their renamed versions), but they - are depricated and will eventually be removed. - -* Moved several function in mlab.py and cbook.py into a separate module - numerical_methods.py because they were unrelated to the initial purpose of - mlab or cbook and appeared more coherent elsewhere. - -Changes for 0.98.1 -================== - -* Removed broken axes3d support and replaced it with a non implemented - error pointing to 0.91.x - -Changes for 0.98.0 -================== - - matplotlib.image.imread now no longer always returns RGBA -- if - the image is luminance or RGB, it will return a MxN or MxNx3 array - if possible. Also uint8 is no longer always forced to float. - - Rewrote the cm.ScalarMappable callback infrastructure to use - cbook.CallbackRegistry rather than custom callback handling. Amy - users of add_observer/notify of the cm.ScalarMappable should uae - the cm.ScalarMappable.callbacksSM CallbackRegistry instead. - - New axes function and Axes method provide control over the plot - color cycle: axes.set_default_color_cycle(clist) and - Axes.set_color_cycle(clist). - - matplotlib now requires python2.4, so matplotlib.cbook will no - loner provide set, enumerate, reversed or izip compatability functions - - In numpy 1.0 bins are specified by the left edges only. The axes - method "hist" now uses future numpy 1.3 semantic for histograms. - Providing binedges, the last value gives the upper-right edge now, - which was implicitly set to +infinity in numpy 1.0. This also means - that the last bin doesn't contain upper outliers any more by default. - - New axes method and pyplot function, hexbin, is an alternative - to scatter for large datasets. It makes something like a - pcolor of a 2-D histogram, but uses hexagonal bins. - - New kwarg, "symmetric", in MaxNLocator - allows one require an axis to be centered on zero. - - toolkits must now be imported from mpl_toolkits (not matplotlib.toolkits) - -TRANSFORMS REFACTORING - - The primary goal of this refactoring was to make it easier to - extend matplotlib to support new kinds of projections. This is - primarily an internal improvement, and the possible user-visible - changes it allows are yet to come. - - See transforms.py for a description of the design of the new - transformation framework. - - For efficiency, many of these functions return views into Numpy - arrays. This means that if you hold on to a reference to them, - their contents may change. If you want to store a snapshot of - their current values, use the Numpy array method copy(). - - The view intervals are now stored only in one place -- in the Axes - instance, not in the formatter instances as well. This means - formatters must get their limits from their Axis, which in turn - looks up its limits from the Axes. If a Locator is used - temporarily and not assigned to an Axis or Axes, (e.g. in - contour.py), a dummy axis must be created to store its bounds. - Call Locator.create_dummy_axis() to do so. - - The functionality of Pbox has been merged with Bbox. Its methods - now all return copies rather than modifying in place. - - The following lists many of the simple changes necessary to update - code from the old transformation framework to the new one. In - particular, methods that return a copy are named with a verb in - the past tense, whereas methods that alter an object in place are - named with a very in the present tense. - - transforms.py - Bbox.get_bounds() Bbox.bounds - - Bbox.width() Bbox.width - - Bbox.height() Bbox.height - - Bbox.intervalx().get_bounds() Bbox.intervalx - Bbox.intervalx().set_bounds() - [Bbox.intervalx is now a property.] - - Bbox.intervaly().get_bounds() Bbox.intervaly - Bbox.intervaly().set_bounds() - [Bbox.intervaly is now a property.] - - Bbox.xmin() Bbox.x0 or Bbox.xmin - Bbox.ymin() Bbox.y0 or Bbox.ymin - Bbox.xmax() Bbox.x1 or Bbox.xmax - Bbox.ymax() Bbox.y1 or Bbox.ymax - [The Bbox is bound by the points (x0, y0) to (x1, y1) and - there is no defined order to these points, that is, x0 is not - necessarily the left edge of the box. To get the left edge of - the Bbox, use the read-only property xmin.] - - Bbox.overlaps(bboxes) Bbox.count_overlaps(bboxes) - - bbox_all(bboxes) Bbox.union(bboxes) - [Bbox.union is a staticmethod.] - - lbwh_to_bbox(l, b, w, h) Bbox.from_bounds(x0, y0, w, h) - - inverse_transform_bbox(trans, bbox) bbox.inverse_transformed(trans) - - Interval.contains_open(v) interval_contains_open(tuple, v) - Interval.contains(v) interval_contains_open(tuple, v) - - identity_transform() IdentityTransform() - - blend_xy_sep_transform(xtrans, ytrans) blended_transform_factory(xtrans, ytrans) - - scale_transform(xs, ys) Affine2D().scale(xs[, ys]) - - get_bbox_transform(boxin, boxout) BboxTransform(boxin, boxout) or - BboxTransformFrom(boxin) or - BboxTransformTo(boxout) - - Transform.seq_xy_tup(points) Transform.transform(points) - - Transform.inverse_xy_tup(points) Transform.inverted().transform(points) - - axes.py - Axes.get_position() Axes.get_position() - [Axes.get_position() used to return a list of points, not it - returns a transforms.Bbox instance.] - - Axes.set_position() Axes.set_position() - [Axes.set_position() now accepts either four scalars or a - transforms Bbox instance.] - - [also returns a Bbox] - Axes.toggle_log_lineary() Axes.set_yscale() - [Since the recfactoring allows for more than two scale types - ('log' or 'linear'), it no longer makes sense to have a - toggle. Axes.toggle_log_lineary() has been removed.] - - Axes.hlines(linestyle=) Axes.hlines(linestyles=) - Axes.vlines(linestyle=) Axes.vlines(linestyles=) - [The kwarg 'linestyle' has been replaced with 'linestyles', - which accepts either a single linestyle or a list of - linestyles to use.] - - Subplot class is gone -- now there is only SubplotBase. - - The Polar class has moved to projections/polar.py - - artist.py - Artist.set_clip_path(path) Artist.set_clip_path(path, transform) - [set_clip_path now accepts a path.Path instance and a - transformation that will be applied to the path immediately - before clipping.] - - collections.py - linestyle linestyles - [Linestyles are now treated like all other collection - attributes -- a single value or multiple values may be - provided.] - - colors.py - ColorConvertor.to_rgba_list(c) ColorConvertor.to_rgba_array(c) - [ColorConvertor.to_rgba_array(c) returns an Nx4 Numpy array of - RGBA color quadruples.] - - contour.py - Contour._segments Contour.get_paths() - [Contour.get_paths() now returns a list of path.Path instances.] - - figure.py - Figure.dpi.get()/set() Figure.dpi (a property) - - patches.py - get_verts() get_path() - [Patch.get_path() returns a path.Path instance.] - - backend_bases.py - GraphicsContext.set_clip_rectangle(tuple) GraphicsContext.set_clip_rectangle(bbox) - - GraphicsContext.get_clip_path() GraphicsContext.get_clip_path() - [GraphicsContext.get_clip_path() returns a tuple of the form - (path, affine_transform), where path is a path.Path instance - and affine_transform is a transforms.Affine2D instance.] - - GraphicsContext.set_clip_path(clippath) GraphicsContext.set_clip_path(clippath) - [Now accepts only an instance of transforms.TransformedPath.] - - RendererBase class: - **new methods** ---> - draw_path(self, gc, path, transform, rgbFace) - - draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace) - - draw_path_collection(self, master_transform, cliprect, clippath, - clippath_trans, paths, all_transforms, offsets, - offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds) [optional] - - - **changed methods** ---> - draw_image(self, x, y, im, bbox) draw_image(self, x, y, im, bbox, - clippath, clippath_trans) - - **removed methods** ---> - draw_arc - draw_line_collection - draw_line - draw_lines - draw_point - draw_quad_mesh - draw_poly_collection - draw_polygon - draw_rectangle - draw_regpoly_collection - -END OF TRANSFORMS REFACTORING - - - - -0.91.2 Released - - For csv2rec, checkrows=0 is the new default indicating all rows - will be checked for type inference - - A warning is issued when an image is drawn on log-scaled - axes, since it will not log-scale the image data. - - Moved rec2gtk to matplotlib.toolkits.gtktools - - Moved rec2excel to matplotlib.toolkits.exceltools - - Removed, dead/experimental ExampleInfo, Namespace and Importer - code from matplotlib/__init__.py - -0.91.1 Released - -0.91.0 Released - - Changed cbook.is_file_like to cbook.is_writable_file_like and - corrected behavior. - - Added ax kwarg to pyplot.colorbar and Figure.colorbar so that - one can specify the axes object from which space for the colorbar - is to be taken, if one does not want to make the colorbar axes - manually. - - Changed cbook.reversed so it yields a tuple rather than a - (index, tuple). This agrees with the python reversed builtin, - and cbook only defines reversed if python doesnt provide the - builtin. - - Made skiprows=1 the default on csv2rec - - The gd and paint backends have been deleted. - - The errorbar method and function now accept additional kwargs - so that upper and lower limits can be indicated by capping the - bar with a caret instead of a straight line segment. - - The dviread.py file now has a parser for files like psfonts.map - and pdftex.map, to map TeX font names to external files. - - The file type1font.py contains a new class for Type 1 fonts. - Currently it simply reads pfa and pfb format files and stores the - data in a way that is suitable for embedding in pdf files. In the - future the class might actually parse the font to allow e.g. - subsetting. - - FT2Font now supports FT_Attach_File. In practice this can be used - to read an afm file in addition to a pfa/pfb file, to get metrics - and kerning information for a Type 1 font. - - The AFM class now supports querying CapHeight and stem widths. The - get_name_char method now has an isord kwarg like get_width_char. - - Changed pcolor default to shading='flat'; but as noted now in the - docstring, it is preferable to simply use the edgecolor kwarg. - - The mathtext font commands (\cal, \rm, \it, \tt) now behave as TeX - does: they are in effect until the next font change command or the - end of the grouping. Therefore uses of $\cal{R}$ should be - changed to ${\cal R}$. Alternatively, you may use the new - LaTeX-style font commands (\mathcal, \mathrm, \mathit, \mathtt) - which do affect the following group, eg. $\mathcal{R}$. - - Text creation commands have a new default linespacing and - a new linespacing kwarg, which is a multiple of the maximum - vertical extent of a line of ordinary text. The default is - 1.2; linespacing=2 would be like ordinary double spacing, for - example. - - Changed default kwarg in colors.Normalize.__init__ to clip=False; - clipping silently defeats the purpose of the special over, under, - and bad values in the colormap, thereby leading to unexpected - behavior. The new default should reduce such surprises. - - Made the emit property of set_xlim and set_ylim true by default; - removed the Axes custom callback handling into a 'callbacks' - attribute which is a cbook.CallbackRegistry instance. This now - supports the xlim_changed and ylim_changed Axes events. - -0.90.1 released - - The file dviread.py has a (very limited and fragile) dvi reader - for usetex support. The API might change in the future so don't - depend on it yet. - - Removed deprecated support for a float value as a gray-scale; - now it must be a string, like '0.5'. Added alpha kwarg to - ColorConverter.to_rgba_list. - - New method set_bounds(vmin, vmax) for formatters, locators sets - the viewInterval and dataInterval from floats. - - Removed deprecated colorbar_classic. - - Line2D.get_xdata and get_ydata valid_only=False kwarg is replaced - by orig=True. When True, it returns the original data, otherwise - the processed data (masked, converted) - - Some modifications to the units interface. - units.ConversionInterface.tickers renamed to - units.ConversionInterface.axisinfo and it now returns a - units.AxisInfo object rather than a tuple. This will make it - easier to add axis info functionality (eg I added a default label - on this iteration) w/o having to change the tuple length and hence - the API of the client code everytime new functionality is added. - Also, units.ConversionInterface.convert_to_value is now simply - named units.ConversionInterface.convert. - - Axes.errorbar uses Axes.vlines and Axes.hlines to draw its error - limits int he vertical and horizontal direction. As you'll see - in the changes below, these funcs now return a LineCollection - rather than a list of lines. The new return signature for - errorbar is ylins, caplines, errorcollections where - errorcollections is a xerrcollection, yerrcollection - - Axes.vlines and Axes.hlines now create and returns a LineCollection, not a list - of lines. This is much faster. The kwarg signature has changed, - so consult the docs - - MaxNLocator accepts a new Boolean kwarg ('integer') to force - ticks to integer locations. - - Commands that pass an argument to the Text constructor or to - Text.set_text() now accept any object that can be converted - with '%s'. This affects xlabel(), title(), etc. - - Barh now takes a **kwargs dict instead of most of the old - arguments. This helps ensure that bar and barh are kept in sync, - but as a side effect you can no longer pass e.g. color as a - positional argument. - - ft2font.get_charmap() now returns a dict that maps character codes - to glyph indices (until now it was reversed) - - Moved data files into lib/matplotlib so that setuptools' develop - mode works. Re-organized the mpl-data layout so that this source - structure is maintained in the installation. (I.e. the 'fonts' and - 'images' sub-directories are maintained in site-packages.). - Suggest removing site-packages/matplotlib/mpl-data and - ~/.matplotlib/ttffont.cache before installing - -0.90.0 released - - All artists now implement a "pick" method which users should not - call. Rather, set the "picker" property of any artist you want to - pick on (the epsilon distance in points for a hit test) and - register with the "pick_event" callback. See - examples/pick_event_demo.py for details - - Bar, barh, and hist have "log" binary kwarg: log=True - sets the ordinate to a log scale. - - Boxplot can handle a list of vectors instead of just - an array, so vectors can have different lengths. - - Plot can handle 2-D x and/or y; it plots the columns. - - Added linewidth kwarg to bar and barh. - - Made the default Artist._transform None (rather than invoking - identity_transform for each artist only to have it overridden - later). Use artist.get_transform() rather than artist._transform, - even in derived classes, so that the default transform will be - created lazily as needed - - New LogNorm subclass of Normalize added to colors.py. - All Normalize subclasses have new inverse() method, and - the __call__() method has a new clip kwarg. - - Changed class names in colors.py to match convention: - normalize -> Normalize, no_norm -> NoNorm. Old names - are still available for now. - - Removed obsolete pcolor_classic command and method. - - Removed lineprops and markerprops from the Annotation code and - replaced them with an arrow configurable with kwarg arrowprops. - See examples/annotation_demo.py - JDH - -0.87.7 released - - Completely reworked the annotations API because I found the old - API cumbersome. The new design is much more legible and easy to - read. See matplotlib.text.Annotation and - examples/annotation_demo.py - - markeredgecolor and markerfacecolor cannot be configured in - matplotlibrc any more. Instead, markers are generally colored - automatically based on the color of the line, unless marker colors - are explicitely set as kwargs - NN - - Changed default comment character for load to '#' - JDH - - math_parse_s_ft2font_svg from mathtext.py & mathtext2.py now returns - width, height, svg_elements. svg_elements is an instance of Bunch ( - cmbook.py) and has the attributes svg_glyphs and svg_lines, which are both - lists. - - Renderer.draw_arc now takes an additional parameter, rotation. - It specifies to draw the artist rotated in degrees anti- - clockwise. It was added for rotated ellipses. - - Renamed Figure.set_figsize_inches to Figure.set_size_inches to - better match the get method, Figure.get_size_inches. - - Removed the copy_bbox_transform from transforms.py; added - shallowcopy methods to all transforms. All transforms already - had deepcopy methods. - - FigureManager.resize(width, height): resize the window - specified in pixels - - barh: x and y args have been renamed to width and bottom - respectively, and their order has been swapped to maintain - a (position, value) order. - - bar and barh: now accept kwarg 'edgecolor'. - - bar and barh: The left, height, width and bottom args can - now all be scalars or sequences; see docstring. - - barh: now defaults to edge aligned instead of center - aligned bars - - bar, barh and hist: Added a keyword arg 'align' that - controls between edge or center bar alignment. - - Collections: PolyCollection and LineCollection now accept - vertices or segments either in the original form [(x,y), - (x,y), ...] or as a 2D numerix array, with X as the first column - and Y as the second. Contour and quiver output the numerix - form. The transforms methods Bbox.update() and - Transformation.seq_xy_tups() now accept either form. - - Collections: LineCollection is now a ScalarMappable like - PolyCollection, etc. - - Specifying a grayscale color as a float is deprecated; use - a string instead, e.g., 0.75 -> '0.75'. - - Collections: initializers now accept any mpl color arg, or - sequence of such args; previously only a sequence of rgba - tuples was accepted. - - Colorbar: completely new version and api; see docstring. The - original version is still accessible as colorbar_classic, but - is deprecated. - - Contourf: "extend" kwarg replaces "clip_ends"; see docstring. - Masked array support added to pcolormesh. - - Modified aspect-ratio handling: - Removed aspect kwarg from imshow - Axes methods: - set_aspect(self, aspect, adjustable=None, anchor=None) - set_adjustable(self, adjustable) - set_anchor(self, anchor) - Pylab interface: - axis('image') - - Backend developers: ft2font's load_char now takes a flags - argument, which you can OR together from the LOAD_XXX - constants. - -API Changes in matplotlib-0.86 - - Matplotlib data is installed into the matplotlib module. - This is similar to package_data. This should get rid of - having to check for many possibilities in _get_data_path(). - The MATPLOTLIBDATA env key is still checked first to allow - for flexibility. - - 1) Separated the color table data from cm.py out into - a new file, _cm.py, to make it easier to find the actual - code in cm.py and to add new colormaps. Everything - from _cm.py is imported by cm.py, so the split should be - transparent. - 2) Enabled automatic generation of a colormap from - a list of colors in contour; see modified - examples/contour_demo.py. - 3) Support for imshow of a masked array, with the - ability to specify colors (or no color at all) for - masked regions, and for regions that are above or - below the normally mapped region. See - examples/image_masked.py. - 4) In support of the above, added two new classes, - ListedColormap, and no_norm, to colors.py, and modified - the Colormap class to include common functionality. Added - a clip kwarg to the normalize class. - - -API Changes in matplotlib-0.85 - - Made xtick and ytick separate props in rc - - made pos=None the default for tick formatters rather than 0 to - indicate "not supplied" - - Removed "feature" of minor ticks which prevents them from - overlapping major ticks. Often you want major and minor ticks at - the same place, and can offset the major ticks with the pad. This - could be made configurable - - Changed the internal structure of contour.py to a more OO style. - Calls to contour or contourf in axes.py or pylab.py now return - a ContourSet object which contains references to the - LineCollections or PolyCollections created by the call, - as well as the configuration variables that were used. - The ContourSet object is a "mappable" if a colormap was used. - - Added a clip_ends kwarg to contourf. From the docstring: - * clip_ends = True - If False, the limits for color scaling are set to the - minimum and maximum contour levels. - True (default) clips the scaling limits. Example: - if the contour boundaries are V = [-100, 2, 1, 0, 1, 2, 100], - then the scaling limits will be [-100, 100] if clip_ends - is False, and [-3, 3] if clip_ends is True. - Added kwargs linewidths, antialiased, and nchunk to contourf. These - are experimental; see the docstring. - - Changed Figure.colorbar(): - kw argument order changed; - if mappable arg is a non-filled ContourSet, colorbar() shows - lines instead hof polygons. - if mappable arg is a filled ContourSet with clip_ends=True, - the endpoints are not labelled, so as to give the - correct impression of open-endedness. - - Changed LineCollection.get_linewidths to get_linewidth, for - consistency. - - -API Changes in matplotlib-0.84 - - Unified argument handling between hlines and vlines. Both now - take optionally a fmt argument (as in plot) and a keyword args - that can be passed onto Line2D. - - Removed all references to "data clipping" in rc and lines.py since - these were not used and not optimized. I'm sure they'll be - resurrected later with a better implementation when needed. - - 'set' removed - no more deprecation warnings. Use 'setp' instead. - - Backend developers: Added flipud method to image and removed it - from to_str. Removed origin kwarg from backend.draw_image. - origin is handled entirely by the frontend now. - -API Changes in matplotlib-0.83 - - - Made HOME/.matplotlib the new config dir where the matplotlibrc - file, the ttf.cache, and the tex.cache live. The new default - filenames in .matplotlib have no leading dot and are not hidden. - Eg, the new names are matplotlibrc, tex.cache, and ttffont.cache. - This is how ipython does it so it must be right. - - If old files are found, a warning is issued and they are moved to - the new location. - - - backends/__init__.py no longer imports new_figure_manager, - draw_if_interactive and show from the default backend, but puts - these imports into a call to pylab_setup. Also, the Toolbar is no - longer imported from WX/WXAgg. New usage: - - from backends import pylab_setup - new_figure_manager, draw_if_interactive, show = pylab_setup() - - - Moved Figure.get_width_height() to FigureCanvasBase. It now - returns int instead of float. - -API Changes in matplotlib-0.82 - - - toolbar import change in GTKAgg, GTKCairo and WXAgg - - - Added subplot config tool to GTK* backends -- note you must now - import the NavigationToolbar2 from your backend of choice rather - than from backend_gtk because it needs to know about the backend - specific canvas -- see examples/embedding_in_gtk2.py. Ditto for - wx backend -- see examples/embedding_in_wxagg.py - - - - hist bin change - - Sean Richards notes there was a problem in the way we created - the binning for histogram, which made the last bin - underrepresented. From his post: - - I see that hist uses the linspace function to create the bins - and then uses searchsorted to put the values in their correct - bin. Thats all good but I am confused over the use of linspace - for the bin creation. I wouldn't have thought that it does - what is needed, to quote the docstring it creates a "Linear - spaced array from min to max". For it to work correctly - shouldn't the values in the bins array be the same bound for - each bin? (i.e. each value should be the lower bound of a - bin). To provide the correct bins for hist would it not be - something like - - def bins(xmin, xmax, N): - if N==1: return xmax - dx = (xmax-xmin)/N # instead of N-1 - return xmin + dx*arange(N) - - - This suggestion is implemented in 0.81. My test script with these - changes does not reveal any bias in the binning - - from matplotlib.numerix.mlab import randn, rand, zeros, Float - from matplotlib.mlab import hist, mean - - Nbins = 50 - Ntests = 200 - results = zeros((Ntests,Nbins), typecode=Float) - for i in range(Ntests): - print 'computing', i - x = rand(10000) - n, bins = hist(x, Nbins) - results[i] = n - print mean(results) - - -API CHANGES in matplotlib-0.81 - - - pylab and artist "set" functions renamed to setp to avoid clash - with python2.4 built-in set. Current version will issue a - deprecation warning which will be removed in future versions - - - imshow interpolation arguments changes for advanced interpolation - schemes. See help imshow, particularly the interpolation, - filternorm and filterrad kwargs - - - Support for masked arrays has been added to the plot command and - to the Line2D object. Only the valid points are plotted. A - "valid_only" kwarg was added to the get_xdata() and get_ydata() - methods of Line2D; by default it is False, so that the original - data arrays are returned. Setting it to True returns the plottable - points. - - - contour changes: - - Masked arrays: contour and contourf now accept masked arrays as - the variable to be contoured. Masking works correctly for - contour, but a bug remains to be fixed before it will work for - contourf. The "badmask" kwarg has been removed from both - functions. - - Level argument changes: - - Old version: a list of levels as one of the positional - arguments specified the lower bound of each filled region; the - upper bound of the last region was taken as a very large - number. Hence, it was not possible to specify that z values - between 0 and 1, for example, be filled, and that values - outside that range remain unfilled. - - New version: a list of N levels is taken as specifying the - boundaries of N-1 z ranges. Now the user has more control over - what is colored and what is not. Repeated calls to contourf - (with different colormaps or color specifications, for example) - can be used to color different ranges of z. Values of z - outside an expected range are left uncolored. - - Example: - Old: contourf(z, [0, 1, 2]) would yield 3 regions: 0-1, 1-2, and >2. - New: it would yield 2 regions: 0-1, 1-2. If the same 3 regions were - desired, the equivalent list of levels would be [0, 1, 2, - 1e38]. - - -API CHANGES in matplotlib-0.80 - - - xlim/ylim/axis always return the new limits regardless of - arguments. They now take kwargs which allow you to selectively - change the upper or lower limits while leaving unnamed limits - unchanged. See help(xlim) for example - -API CHANGES in matplotlib-0.73 - - - Removed deprecated ColormapJet and friends - - - Removed all error handling from the verbose object - - - figure num of zero is now allowed - -API CHANGES in matplotlib-0.72 - - - Line2D, Text, and Patch copy_properties renamed update_from and - moved into artist base class - - - LineCollecitons.color renamed to LineCollections.set_color for - consistency with set/get introspection mechanism, - - - pylab figure now defaults to num=None, which creates a new figure - with a guaranteed unique number - - - contour method syntax changed - now it is matlab compatible - - unchanged: contour(Z) - old: contour(Z, x=Y, y=Y) - new: contour(X, Y, Z) - - see http://matplotlib.sf.net/matplotlib.pylab.html#-contour - - - - Increased the default resolution for save command. - - - Renamed the base attribute of the ticker classes to _base to avoid conflict - with the base method. Sitt for subs - - - subs=none now does autosubbing in the tick locator. - - - New subplots that overlap old will delete the old axes. If you - do not want this behavior, use fig.add_subplot or the axes - command - -API CHANGES in matplotlib-0.71 - - Significant numerix namespace changes, introduced to resolve - namespace clashes between python built-ins and mlab names. - Refactored numerix to maintain separate modules, rather than - folding all these names into a single namespace. See the following - mailing list threads for more information and background - - http://sourceforge.net/mailarchive/forum.php?thread_id=6398890&forum_id=36187 - http://sourceforge.net/mailarchive/forum.php?thread_id=6323208&forum_id=36187 - - - OLD usage - - from matplotlib.numerix import array, mean, fft - - NEW usage - - from matplotlib.numerix import array - from matplotlib.numerix.mlab import mean - from matplotlib.numerix.fft import fft - - numerix dir structure mirrors numarray (though it is an incomplete - implementation) - - numerix - numerix/mlab - numerix/linear_algebra - numerix/fft - numerix/random_array - - but of course you can use 'numerix : Numeric' and still get the - symbols. - - pylab still imports most of the symbols from Numerix, MLab, fft, - etc, but is more cautious. For names that clash with python names - (min, max, sum), pylab keeps the builtins and provides the numeric - versions with an a* prefix, eg (amin, amax, asum) - - - -API CHANGES in matplotlib-0.70 - - MplEvent factored into a base class Event and derived classes - MouseEvent and KeyEvent - - Removed definct set_measurement in wx toolbar - -API CHANGES in matplotlib-0.65.1 - - removed add_axes and add_subplot from backend_bases. Use - figure.add_axes and add_subplot instead. The figure now manages the - current axes with gca and sca for get and set current axe. If you - have code you are porting which called, eg, figmanager.add_axes, you - can now simply do figmanager.canvas.figure.add_axes. - -API CHANGES in matplotlib-0.65 - - mpl_connect and mpl_disconnect in the matlab interface renamed to - connect and disconnect - - Did away with the text methods for angle since they were ambiguous. - fontangle could mean fontstyle (obligue, etc) or the rotation of the - text. Use style and rotation instead. - - -API CHANGES in matplotlib-0.63 - - Dates are now represented internally as float days since 0001-01-01, - UTC. - - All date tickers and formatters are now in matplotlib.dates, rather - than matplotlib.tickers - - converters have been abolished from all functions and classes. - num2date and date2num are now the converter functions for all date - plots - - Most of the date tick locators have a different meaning in their - constructors. In the prior implementation, the first argument was a - base and multiples of the base were ticked. Eg - - HourLocator(5) # old: tick every 5 minutes - - In the new implementation, the explicit points you want to tick are - provided as a number or sequence - - HourLocator(range(0,5,61)) # new: tick every 5 minutes - - This gives much greater flexibility. I have tried to make the - default constructors (no args) behave similarly, where possible. - - Note that YearLocator still works under the base/multiple scheme. - The difference between the YearLocator and the other locators is - that years are not recurrent. - - - Financial functions: - - matplotlib.finance.quotes_historical_yahoo(ticker, date1, date2) - - date1, date2 are now datetime instances. Return value is a list - of quotes where the quote time is a float - days since gregorian - start, as returned by date2num - - See examples/finance_demo.py for example usage of new API - - -API CHANGES in matplotlib-0.61 - - canvas.connect is now deprecated for event handling. use - mpl_connect and mpl_disconnect instead. The callback signature is - func(event) rather than func(widget, evet) - -API CHANGES in matplotlib-0.60 - -ColormapJet and Grayscale are deprecated. For backwards -compatibility, they can be obtained either by doing - - from matplotlib.cm import ColormapJet - -or - - from matplotlib.matlab import * - -They are replaced by cm.jet and cm.grey - - -API CHANGES in matplotlib-0.54.3 - -removed the set_default_font / get_default_font scheme from the -font_manager to unify customization of font defaults with the rest of -the rc scheme. See examples/font_properties_demo.py and help(rc) in -matplotlib.matlab. - - -API CHANGES in matplotlib-0.54 - -matlab interface -================ - -dpi ---- - -Several of the backends used a PIXELS_PER_INCH hack that I added to -try and make images render consistently across backends. This just -complicated matters. So you may find that some font sizes and line -widths appear different than before. Apologies for the -inconvenience. You should set the dpi to an accurate value for your -screen to get true sizes. - - -pcolor and scatter ------------------- - -There are two changes to the matlab interface API, both involving the -patch drawing commands. For efficiency, pcolor and scatter have been -rewritten to use polygon collections, which are a new set of objects -from matplotlib.collections designed to enable efficient handling of -large collections of objects. These new collections make it possible -to build large scatter plots or pcolor plots with no loops at the -python level, and are significantly faster than their predecessors. -The original pcolor and scatter functions are retained as -pcolor_classic and scatter_classic. - -The return value from pcolor is a PolyCollection. Most of the -propertes that are available on rectangles or other patches are also -available on PolyCollections, eg you can say - - c = scatter(blah, blah) - c.set_linewidth(1.0) - c.set_facecolor('r') - c.set_alpha(0.5) - -or - - c = scatter(blah, blah) - set(c, 'linewidth', 1.0, 'facecolor', 'r', 'alpha', 0.5) - - -Because the collection is a single object, you no longer need to loop -over the return value of scatter or pcolor to set properties for the -entire list. - -If you want the different elements of a collection to vary on a -property, eg to have different line widths, see matplotlib.collections -for a discussion on how to set the properties as a sequence. - -For scatter, the size argument is now in points^2 (the area of the -symbol in points) as in matlab and is not in data coords as before. -Using sizes in data coords caused several problems. So you will need -to adjust your size arguments accordingly or use scatter_classic. - -mathtext spacing ----------------- - -For reasons not clear to me (and which I'll eventually fix) spacing no -longer works in font groups. However, I added three new spacing -commands which compensate for this '\ ' (regular space), '\/' (small -space) and '\hspace{frac}' where frac is a fraction of fontsize in -points. You will need to quote spaces in font strings, is - - title(r'$\rm{Histogram\ of\ IQ:}\ \mu=100,\ \sigma=15$') - - - -Object interface - Application programmers -========================================== - -Autoscaling ------------- - - The x and y axis instances no longer have autoscale view. These are - handled by axes.autoscale_view - -Axes creation --------------- - - You should not instantiate your own Axes any more using the OO API. - Rather, create a Figure as before and in place of - - f = Figure(figsize=(5,4), dpi=100) - a = Subplot(f, 111) - f.add_axis(a) - - use - - f = Figure(figsize=(5,4), dpi=100) - a = f.add_subplot(111) - - That is, add_axis no longer exists and is replaced by - - add_axes(rect, axisbg=defaultcolor, frameon=True) - add_subplot(num, axisbg=defaultcolor, frameon=True) - -Artist methods ---------------- - - If you define your own Artists, you need to rename the _draw method - to draw - -Bounding boxes --------------- - - matplotlib.transforms.Bound2D is replaced by - matplotlib.transforms.Bbox. If you want to construct a bbox from - left, bottom, width, height (the signature for Bound2D), use - matplotlib.transforms.lbwh_to_bbox, as in - - bbox = clickBBox = lbwh_to_bbox(left, bottom, width, height) - - The Bbox has a different API than the Bound2D. Eg, if you want to - get the width and height of the bbox - - OLD - width = fig.bbox.x.interval() - height = fig.bbox.y.interval() - - New - width = fig.bbox.width() - height = fig.bbox.height() - - - - -Object constructors -------------------- - - You no longer pass the bbox, dpi, or transforms to the various - Artist constructors. The old way or creating lines and rectangles - was cumbersome because you had to pass so many attributes to the - Line2D and Rectangle classes not related directly to the gemoetry - and properties of the object. Now default values are added to the - object when you call axes.add_line or axes.add_patch, so they are - hidden from the user. - - If you want to define a custom transformation on these objects, call - o.set_transform(trans) where trans is a Transformation instance. - - In prior versions of you wanted to add a custom line in data coords, - you would have to do - - l = Line2D(dpi, bbox, x, y, - color = color, - transx = transx, - transy = transy, - ) - - now all you need is - - l = Line2D(x, y, color=color) - - and the axes will set the transformation for you (unless you have - set your own already, in which case it will eave it unchanged) - -Transformations ---------------- - - The entire transformation architecture has been rewritten. - Previously the x and y transformations where stored in the xaxis and - yaxis insstances. The problem with this approach is it only allows - for separable transforms (where the x and y transformations don't - depend on one another). But for cases like polar, they do. Now - transformations operate on x,y together. There is a new base class - matplotlib.transforms.Transformation and two concrete - implemetations, matplotlib.transforms.SeparableTransformation and - matplotlib.transforms.Affine. The SeparableTransformation is - constructed with the bounding box of the input (this determines the - rectangular coordinate system of the input, ie the x and y view - limits), the bounding box of the display, and possibily nonlinear - transformations of x and y. The 2 most frequently used - transformations, data cordinates -> display and axes coordinates -> - display are available as ax.transData and ax.transAxes. See - alignment_demo.py which uses axes coords. - - Also, the transformations should be much faster now, for two reasons - - * they are written entirely in extension code - - * because they operate on x and y together, they can do the entire - transformation in one loop. Earlier I did something along the - lines of - - xt = sx*func(x) + tx - yt = sy*func(y) + ty - - Although this was done in numerix, it still involves 6 length(x) - for-loops (the multiply, add, and function evaluation each for x - and y). Now all of that is done in a single pass. - - - If you are using transformations and bounding boxes to get the - cursor position in data coordinates, the method calls are a little - different now. See the updated examples/coords_demo.py which shows - you how to do this. - - Likewise, if you are using the artist bounding boxes to pick items - on the canvas with the GUI, the bbox methods are somewhat - different. You will need to see the updated - examples/object_picker.py. - - See unit/transforms_unit.py for many examples using the new - transformations. - - - -API changes at 0.50 - - * refactored Figure class so it is no longer backend dependent. - FigureCanvasBackend takes over the backend specific duties of the - Figure. matplotlib.backend_bases.FigureBase moved to - matplotlib.figure.Figure. - - * backends must implement FigureCanvasBackend (the thing that - controls the figure and handles the events if any) and - FigureManagerBackend (wraps the canvas and the window for matlab - interface). FigureCanvasBase implements a backend switching - mechanism - - * Figure is now an Artist (like everything else in the figure) and - is totally backend independent - - * GDFONTPATH renamed to TTFPATH - - * backend faceColor argument changed to rgbFace - - * colormap stuff moved to colors.py - - * arg_to_rgb in backend_bases moved to class ColorConverter in - colors.py - - * GD users must upgrade to gd-2.0.22 and gdmodule-0.52 since new gd - features (clipping, antialiased lines) are now used. - - * Renderer must implement points_to_pixels - -Migrating code: - - Matlab interface: - - The only API change for those using the matlab interface is in how - you call figure redraws for dynamically updating figures. In the - old API, you did - - fig.draw() - - In the new API, you do - - manager = get_current_fig_manager() - manager.canvas.draw() - - See the examples system_monitor.py, dynamic_demo.py, and anim.py - - API - - There is one important API change for application developers. - Figure instances used subclass GUI widgets that enabled them to be - placed directly into figures. Eg, FigureGTK subclassed - gtk.DrawingArea. Now the Figure class is independent of the - backend, and FigureCanvas takes over the functionality formerly - handled by Figure. In order to include figures into your apps, - you now need to do, for example - - # gtk example - fig = Figure(figsize=(5,4), dpi=100) - canvas = FigureCanvasGTK(fig) # a gtk.DrawingArea - canvas.show() - vbox.pack_start(canvas) - - If you use the NavigationToolbar, this in now intialized with a - FigureCanvas, not a Figure. The examples embedding_in_gtk.py, - embedding_in_gtk2.py, and mpl_with_glade.py all reflect the new - API so use these as a guide. - - All prior calls to - - figure.draw() and - figure.print_figure(args) - - should now be - - canvas.draw() and - canvas.print_figure(args) - - Apologies for the inconvenience. This refactorization brings - significant more freedom in developing matplotlib and should bring - better plotting capabilities, so I hope the inconvenience is worth - it. - - - -API changes at 0.42 - - * Refactoring AxisText to be backend independent. Text drawing and - get_window_extent functionality will be moved to the Renderer. - - * backend_bases.AxisTextBase is now text.Text module - - * All the erase and reset functionality removed frmo AxisText - not - needed with double buffered drawing. Ditto with state change. - Text instances have a get_prop_tup method that returns a hashable - tuple of text properties which you can use to see if text props - have changed, eg by caching a font or layout instance in a dict - with the prop tup as a key -- see RendererGTK.get_pango_layout in - backend_gtk for an example. - - * Text._get_xy_display renamed Text.get_xy_display - - * Artist set_renderer and wash_brushes methods removed - - * Moved Legend class from matplotlib.axes into matplotlib.legend - - * Moved Tick, XTick, YTick, Axis, XAxis, YAxis from matplotlib.axes - to matplotlib.axis - - * moved process_text_args to matplotlib.text - - * After getting Text handled in a backend independent fashion, the - import process is much cleaner since there are no longer cyclic - dependencies - - * matplotlib.matlab._get_current_fig_manager renamed to - matplotlib.matlab.get_current_fig_manager to allow user access to - the GUI window attribute, eg figManager.window for GTK and - figManager.frame for wx - - - - - -API changes at 0.40 - -- Artist - * __init__ takes a DPI instance and a Bound2D instance which is - the bounding box of the artist in display coords - * get_window_extent returns a Bound2D instance - * set_size is removed; replaced by bbox and dpi - * the clip_gc method is removed. Artists now clip themselves with - their box - * added _clipOn boolean attribute. If True, gc clip to bbox. - -- AxisTextBase - * Initialized with a transx, transy which are Transform instances - * set_drawing_area removed - * get_left_right and get_top_bottom are replaced by get_window_extent - -- Line2D Patches now take transx, transy - * Initialized with a transx, transy which are Transform instances - -- Patches - * Initialized with a transx, transy which are Transform instances - -- FigureBase attributes dpi is a DPI intance rather than scalar and - new attribute bbox is a Bound2D in display coords, and I got rid of - the left, width, height, etc... attributes. These are now - accessible as, for example, bbox.x.min is left, bbox.x.interval() is - width, bbox.y.max is top, etc... - -- GcfBase attribute pagesize renamed to figsize - -- Axes - * removed figbg attribute - * added fig instance to __init__ - * resizing is handled by figure call to resize. - -- Subplot - * added fig instance to __init__ - -- Renderer methods for patches now take gcEdge and gcFace instances. - gcFace=None takes the place of filled=False - -- True and False symbols provided by cbook in a python2.3 compatible - way - -- new module transforms supplies Bound1D, Bound2D and Transform - instances and more - -- Changes to the matlab helpers API - - * _matlab_helpers.GcfBase is renamed by Gcf. Backends no longer - need to derive from this class. Instead, they provide a factory - function new_figure_manager(num, figsize, dpi). The destroy - method of the GcfDerived from the backends is moved to the derived - FigureManager. - - * FigureManagerBase moved to backend_bases - - * Gcf.get_all_figwins renamed to Gcf.get_all_fig_managers - -Jeremy: - - Make sure to self._reset = False in AxisTextWX._set_font. This was - something missing in my backend code. Added: trunk/matplotlib/doc/api/api_changes.rst =================================================================== --- trunk/matplotlib/doc/api/api_changes.rst (rev 0) +++ trunk/matplotlib/doc/api/api_changes.rst 2008-11-19 14:45:44 UTC (rev 6415) @@ -0,0 +1,1497 @@ +=========== +API Changes +=========== + +This chapter is a log of changes to matplotlib that affect the +outward-facing API. If updating matplotlib breaks your scripts, this +list may help describe what changes may be necessary in your code. + +Changes for 0.98.x +================== + +* Font lookup now uses a nearest-neighbor approach rather than an + exact match. Some fonts may be different in plots, but should be + closer to what was requested. + +* :meth:`matplotlib.axes.Axes.set_xlim`, + :meth:`matplotlib.axes.Axes.set_ylim` now return a copy of the + :attr:`viewlim` array to avoid modify-in-place surprises. + +* :meth:`matplotlib.afm.AFM.get_fullname` and + :meth:`matplotlib.afm.AFM.get_familyname` no longer raise an + exception if the AFM file does not specify these optional + attributes, but returns a guess based on the required FontName + attribute. + +* Changed precision kwarg in :func:`matplotlib.pyplot.spy`; default is + 0, and the string value 'present' is used for sparse arrays only to + show filled locations. + +* :class:`matplotlib.collections.EllipseCollection` added. + +* Added ``angles`` kwarg to :func:`matplotlib.pyplot.quiver` for more + flexible specification of the arrow angles. + +* Deprecated (raise NotImplementedError) all the mlab2 functions from + :mod:`matplotlib.mlab` out of concern that some of them were not + clean room implementations. + +* Methods :meth:`matplotlib.collections.Collection.get_offsets` and + :meth:`matplotlib.collections.Collection.set_offsets` added to + :class:`~matplotlib.collections.Collection` base class. + +* :attr:`matplotlib.figure.Figure.figurePatch` renamed + :attr:`matplotlib.figure.Figure.patch`; + :attr:`matplotlib.axes.Axes.axesPatch` renamed + :attr:`matplotlib.axes.Axes.patch`; + :attr:`matplotlib.axes.Axes.axesFrame` renamed + :attr:`matplotlib.axes.Axes.frame`. + :meth:`matplotlib.axes.Axes.get_frame`, which returns + :attr:`matplotlib.axes.Axes.patch`, is deprecated. + +* Changes in the :class:`matplotlib.contour.ContourLabeler` attributes + (:func:`matplotlib.pyplot.clabel` function) so that they all have a + form like ``.labelAttribute``. The three attributes that are most + likely to be used by end users, ``.cl``, ``.cl_xy`` and + ``.cl_cvalues`` have been maintained for the moment (in addition to + their renamed versions), but they are deprecated and will eventually + be removed. + +* Moved several functions in :mod:`matplotlib.mlab` and + :mod:`matplotlib.cbook` into a separate module + :mod:`matplotlib.numerical_methods` because they were unrelated to + the initial purpose of mlab or cbook and appeared more coherent + elsewhere. + +Changes for 0.98.1 +================== + +* Removed broken :mod:`matplotlib.axes3d` support and replaced it with + a non-implemented error pointing to 0.91.x + +Changes for 0.98.0 +================== + +* :func:`matplotlib.image.imread` now no longer always returns RGBA data---if + the image is luminance or RGB, it will return a MxN or MxNx3 array + if possible. Also uint8 is no longer always forced to float. + +* Rewrote the :class:`matplotlib.cm.ScalarMappable` callback + infrastructure to use :class:`matplotlib.cbook.CallbackRegistry` + rather than custom callback handling. Any users of + :meth:`matplotlib.cm.ScalarMappable.add_observer` of the + :class:`~matplotlib.cm.ScalarMappable` should use the + :attr:`matplotlib.cm.ScalarMappable.callbacks` + :class:`~matplotlib.cbook.CallbackRegistry` instead. + +* New axes function and Axes method provide control over the plot + color cycle: :func:`matplotlib.axes.set_default_color_cycle` and + :meth:`matplotlib.axes.Axes.set_color_cycle`. + +* matplotlib now requires Python 2.4, so :mod:`matplotlib.cbook` will + no longer provide :class:`set`, :func:`enumerate`, :func:`reversed` + or :func:`izip` compatibility functions. + +* In Numpy 1.0, bins are specified by the left edges only. The axes + method :meth:`matplotlib.axes.Axes.hist` now uses future Numpy 1.3 + semantics for histograms. Providing ``binedges``, the last value gives + the upper-right edge now, which was implicitly set to +infinity in + Numpy 1.0. This also means that the last bin doesn't contain upper + outliers any more by default. + +* New axes method and pyplot function, + :func:`~matplotlib.pyplot.hexbin`, is an alternative to + :func:`~matplotlib.pyplot.scatter` for large datasets. It makes + something like a :func:`~matplotlib.pyplot.pcolor` of a 2-D + histogram, but uses hexagonal bins. + +* New kwarg, ``symmetric``, in :class:`matplotlib.ticker.MaxNLocator` + allows one require an axis to be centered around zero. + +* Toolkits must now be imported from ``mpl_toolkits`` (not ``matplotlib.toolkits``) + +Notes about the transforms refactoring +-------------------------------------- + +A major new feature of the 0.98 series is a more flexible and +extensible transformation infrastructure, written in Python/Numpy +rather than a custom C extension. + +The primary goal of this refactoring was to make it easier to +extend matplotlib to support new kinds of projections. This is +mostly an internal improvement, and the possible user-visible +changes it allows are yet to come. + +See :mod:`matplotlib.transforms` for a description of the design of +the new transformation framework. + +For efficiency, many of these functions return views into Numpy +arrays. This means that if you hold on to a reference to them, +their contents may change. If you want to store a snapshot of +their current values, use the Numpy array method copy(). + +The view intervals are now stored only in one place -- in the +:class:`matplotlib.axes.Axes` instance, not in the locator instances +as well. This means locators must get their limits from their +:class:`matplotlib.axis.Axis`, which in turn looks up its limits from +the :class:`~matplotlib.axes.Axes`. If a locator is used temporarily +and not assigned to an Axis or Axes, (e.g. in +:mod:`matplotlib.contour`), a dummy axis must be created to store its +bounds. Call :meth:`matplotlib.ticker.Locator.create_dummy_axis` to +do so. + +The functionality of :class:`Pbox` has been merged with +:class:`~matplotlib.transforms.Bbox`. Its methods now all return +copies rather than modifying in place. + +The following lists many of the simple changes necessary to update +code from the old transformation framework to the new one. In +particular, methods that return a copy are named with a verb in the +past tense, whereas methods that alter an object in place are named +with a verb in the present tense. + +:mod:`matplotlib.transforms` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============================================================ ============================================================ +Old method New method +============================================================ ============================================================ +:meth:`Bbox.get_bounds` :attr:`transforms.Bbox.bounds` +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.width` :attr:`transforms.Bbox.width` +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.height` :attr:`transforms.Bbox.height` +------------------------------------------------------------ ------------------------------------------------------------ +`Bbox.intervalx().get_bounds()` :attr:`transforms.Bbox.intervalx` +`Bbox.intervalx().set_bounds()` [:attr:`Bbox.intervalx` is now a property.] +------------------------------------------------------------ ------------------------------------------------------------ +`Bbox.intervaly().get_bounds()` :attr:`transforms.Bbox.intervaly` +`Bbox.intervaly().set_bounds()` [:attr:`Bbox.intervaly` is now a property.] +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.xmin` :attr:`transforms.Bbox.x0` or + :attr:`transforms.Bbox.xmin` [1]_ +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.ymin` :attr:`transforms.Bbox.y0` or + :attr:`transforms.Bbox.ymin` [1]_ +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.xmax` :attr:`transforms.Bbox.x1` or + :attr:`transforms.Bbox.xmax` [1]_ +------------------------------------------------------------ ------------------------------------------------------------ +:meth:`Bbox.ymax` :attr:`transforms.Bbox.y1` or + :attr:`transforms.Bbox.ymax` [1]_ +------------------------------------------------------------ ------------------------------------------------------------ +`Bbox.overlaps(bboxes)` `Bbox.count_overlaps(bboxes)` +------------------------------------------------------------ ------------------------------------------------------------ +`bbox_all(bboxes)` `Bbox.union(bboxes)` + [:meth:`transforms.Bbox.union` is a staticmethod.] +------------------------------------------------------------ ------------------------------------------------------------ +`lbwh_to_bbox(l, b, w, h)` `Bbox.from_bounds(x0, y0, w, h)` + [:meth:`transforms.Bb... [truncated message content] |
From: <jd...@us...> - 2008-11-20 18:58:57
|
Revision: 6423 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6423&view=rev Author: jdh2358 Date: 2008-11-20 18:58:54 +0000 (Thu, 20 Nov 2008) Log Message: ----------- added some helper functions for poly collections and masked regions Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/lib/matplotlib/mlab.py trunk/matplotlib/src/_backend_agg.cpp Added Paths: ----------- trunk/matplotlib/examples/api/filled_masked_regions.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/CHANGELOG 2008-11-20 18:58:54 UTC (rev 6423) @@ -1,3 +1,11 @@ +2008-11-20 Added some static helper methods + BrokenHBarCollection.span_masked and + PolyCollection.fill_between_masked for visualizing + non-masked regions. In the longer term, the better + solution will be to fix the relevant classes and functions + to handle masked data, so this may be a temporary solution + - JDH + 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 Added: trunk/matplotlib/examples/api/filled_masked_regions.py =================================================================== --- trunk/matplotlib/examples/api/filled_masked_regions.py (rev 0) +++ trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -0,0 +1,45 @@ +""" +Illustrate some helper functions for shading regions where a logical +mask is True +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.collections as collections + + +t = np.arange(0.0, 2, 0.01) +s = np.sin(2*np.pi*t) + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using fill_between_masked') +ax.plot(t, s, '-') +ax.axhline(0, color='black', lw=2) + +collection = collections.PolyCollection.fill_between_masked(t, s, s>=0, yboundary=0, color='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.PolyCollection.fill_between_masked(t, s, s<=0, yboundary=0, color='red', alpha=0.5) +ax.add_collection(collection) + + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using span_masked') +ax.plot(t, s, '-') +ax.axhline(0, color='black', lw=2) + +collection = collections.BrokenBarHCollection.span_masked(t, s>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.BrokenBarHCollection.span_masked(t, s<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) +ax.add_collection(collection) + + + +plt.show() + + + + + Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -19,6 +19,7 @@ import matplotlib.artist as artist import matplotlib.backend_bases as backend_bases import matplotlib.path as mpath +import matplotlib.mlab as mlab class Collection(artist.Artist, cm.ScalarMappable): """ @@ -234,7 +235,7 @@ self._urls = [None,] else: self._urls = urls - + def get_urls(self): return self._urls def set_offsets(self, offsets): @@ -671,6 +672,49 @@ for x in self._sizes] return Collection.draw(self, renderer) + + @staticmethod + def fill_between_masked(x, y, mask, yboundary=0, **kwargs): + """ + Create a :class:`PolyCollection` filling the regions between *y* + and *yboundary7* where ``mask==True`` + + + *x* + an N length np array of the x data + + *y* + an N length np array of the y data + + *mask* + an N length numpy boolean array + + *yboundary* + a scalar to fill between *y* and the boundary + + *kwargs* + keyword args passed on to the :class:`PolyCollection` + + """ + polys = [] + for ind0, ind1 in mlab.contiguous_regions(mask): + theseverts = [] + xslice = x[ind0:ind1] + yslice = y[ind0:ind1] + N = len(xslice) + X = np.zeros((2*N+2, 2), np.float) + X[0] = xslice[0], yboundary + X[N+1] = xslice[-1], yboundary + X[1:N+1,0] = xslice + X[1:N+1,1] = yslice + X[N+2:,0] = xslice[::-1] + X[N+2:,1] = yboundary + + polys.append(X) + + collection = PolyCollection(polys, **kwargs) + return collection + class BrokenBarHCollection(PolyCollection): """ A collection of horizontal bars spanning *yrange* with a sequence of @@ -692,6 +736,25 @@ PolyCollection.__init__(self, verts, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + + @staticmethod + def span_masked(x, mask, ymin, ymax, **kwargs): + """ + Create a BrokenBarHCollection to plot horizontal bars from + over the regions in *x* where *mask* is True. The bars range + on the y-axis from *ymin* to *ymax* + + A :class:`BrokenBarHCollection` is returned. + **kwargs are passed on to the collection + """ + xranges = [] + for ind0, ind1 in mlab.contiguous_regions(mask): + xslice = x[ind0:ind1] + xranges.append((xslice[0], xslice[-1]-xslice[0])) + + collection = BrokenBarHCollection(xranges, [ymin, ymax-ymin], **kwargs) + return collection + class RegularPolyCollection(Collection): """Draw a collection of regular polygons with *numsides*.""" _path_generator = mpath.Path.unit_regular_polygon Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-20 18:58:54 UTC (rev 6423) @@ -159,7 +159,7 @@ import csv, warnings, copy, os import numpy as np - +ma = np.ma from matplotlib import verbose import matplotlib.nxutils as nxutils @@ -247,7 +247,7 @@ #The checks for if y is x are so that we can use the same function to #implement the core of psd(), csd(), and spectrogram() without doing #extra calculations. We return the unaveraged Pxy, freqs, and t. - + #Make sure we're dealing with a numpy array. If y and x were the same #object to start with, keep them that way same_data = y is x @@ -309,7 +309,7 @@ Pxy /= (np.abs(windowVals)**2).sum() t = 1./Fs * (ind + NFFT / 2.) freqs = float(Fs) / pad_to * np.arange(numFreqs) - + return Pxy, freqs, t #Split out these keyword docs so that they can be used elsewhere @@ -2104,7 +2104,8 @@ def csv2rec(fname, comments='#', skiprows=0, checkrows=0, delimiter=',', - converterd=None, names=None, missing='', missingd=None): + converterd=None, names=None, missing='', missingd=None, + use_mrecords=True): """ Load data from comma/space/tab delimited file in *fname* into a numpy record array and return the record array. @@ -2139,9 +2140,11 @@ be masked, e.g. '0000-00-00' or 'unused' - *missing*: a string whose value signals a missing field regardless of - the column it appears in, e.g. 'unused' + the column it appears in - If no rows are found, *None* is returned -- see :file:`examples/loadrec.py` + - *use_mrecords*: if True, return an mrecords.fromrecords record array if any of the data are missing + + If no rows are found, *None* is returned -- see :file:`examples/loadrec.py` """ if converterd is None: @@ -2338,7 +2341,8 @@ if not len(rows): return None - if np.any(rowmasks): + + if use_mrecords and np.any(rowmasks): try: from numpy.ma import mrecords except ImportError: raise RuntimeError('numpy 1.05 or later is required for masked array support') @@ -2938,19 +2942,25 @@ xv, yv = poly_below(0, x, y) ax.fill(xv, yv) """ - xs = np.asarray(xs) - ys = np.asarray(ys) + if ma.isMaskedArray(xs) or ma.isMaskedArray(ys): + nx = ma + else: + nx = np + + xs = nx.asarray(xs) + ys = nx.asarray(ys) Nx = len(xs) Ny = len(ys) assert(Nx==Ny) - x = xmin*np.ones(2*Nx) - y = np.ones(2*Nx) + x = xmin*nx.ones(2*Nx) + y = nx.ones(2*Nx) x[:Nx] = xs y[:Nx] = ys y[Nx:] = ys[::-1] return x, y + def poly_between(x, ylower, yupper): """ Given a sequence of *x*, *ylower* and *yupper*, return the polygon @@ -2961,17 +2971,23 @@ Return value is *x*, *y* arrays for use with :meth:`matplotlib.axes.Axes.fill`. """ + if ma.isMaskedArray(ylower) or ma.isMaskedArray(yupper) or ma.isMaskedArray(x): + nx = ma + else: + nx = np + Nx = len(x) if not cbook.iterable(ylower): - ylower = ylower*np.ones(Nx) + ylower = ylower*nx.ones(Nx) if not cbook.iterable(yupper): - yupper = yupper*np.ones(Nx) + yupper = yupper*nx.ones(Nx) - x = np.concatenate( (x, x[::-1]) ) - y = np.concatenate( (yupper, ylower[::-1]) ) + x = nx.concatenate( (x, x[::-1]) ) + y = nx.concatenate( (yupper, ylower[::-1]) ) return x,y + def is_closed_polygon(X): """ Tests whether first and last object in a sequence are the same. These are @@ -2980,6 +2996,28 @@ """ return np.all(X[0] == X[-1]) + +def contiguous_regions(mask): + """ + return a list of (ind0, ind1) such that mask[ind0:ind1].all() is + True and we cover all such regions + + TODO: this is a pure python implementation which probably has a much faster numpy impl + """ + + in_region = None + boundaries = [] + for i, val in enumerate(mask): + if in_region is None and val: + in_region = i + elif in_region is not None and not val: + boundaries.append((in_region, i)) + in_region = None + + if in_region is not None: + boundaries.append((in_region, i+1)) + return boundaries + ################################################## # Vector and path length geometry calculations ################################################## Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 15:46:46 UTC (rev 6422) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 18:58:54 UTC (rev 6423) @@ -385,7 +385,8 @@ if (!py_convert_bbox(box_obj.ptr(), l, b, r, t)) throw Py::TypeError("Invalid bbox provided to copy_from_bbox"); - agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b); + // std::cout << l << " " << b << " " << r << " " << t << " " << (height - (int)b) << " " << height - (int)t << std::endl; + agg::rect_i rect((int)l, height - (int)b, (int)r, height - (int)t); BufferRegion* reg = NULL; try { @@ -419,9 +420,11 @@ BufferRegion* region = static_cast<BufferRegion*>(args[0].ptr()); if (region->data==NULL) - return Py::Object(); - //throw Py::ValueError("Cannot restore_region from NULL data"); + throw Py::ValueError("Cannot restore_region from NULL data"); + //return Py::Object(); + //std::cout << "restoring " << region->width << " " << region->height << " " << region->stride << " " << region->rect.x1 << " " << region->rect.y1 << std::endl; + agg::rendering_buffer rbuf; rbuf.attach(region->data, region->width, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-20 20:22:13
|
Revision: 6424 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6424&view=rev Author: jdh2358 Date: 2008-11-20 20:22:08 +0000 (Thu, 20 Nov 2008) Log Message: ----------- reverted a bug I introduced when toying around with copy region in agg Modified Paths: -------------- trunk/matplotlib/examples/event_handling/path_editor.py trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/src/_backend_agg.cpp Modified: trunk/matplotlib/examples/event_handling/path_editor.py =================================================================== --- trunk/matplotlib/examples/event_handling/path_editor.py 2008-11-20 18:58:54 UTC (rev 6423) +++ trunk/matplotlib/examples/event_handling/path_editor.py 2008-11-20 20:22:08 UTC (rev 6424) @@ -1,3 +1,5 @@ +import matplotlib +matplotlib.use('TkAgg') import numpy as np import matplotlib.path as mpath import matplotlib.patches as mpatches Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 18:58:54 UTC (rev 6423) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-20 20:22:08 UTC (rev 6424) @@ -701,6 +701,9 @@ theseverts = [] xslice = x[ind0:ind1] yslice = y[ind0:ind1] + if not len(xslice): + continue + N = len(xslice) X = np.zeros((2*N+2, 2), np.float) X[0] = xslice[0], yboundary @@ -750,6 +753,8 @@ xranges = [] for ind0, ind1 in mlab.contiguous_regions(mask): xslice = x[ind0:ind1] + if not len(xslice): + continue xranges.append((xslice[0], xslice[-1]-xslice[0])) collection = BrokenBarHCollection(xranges, [ymin, ymax-ymin], **kwargs) Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 18:58:54 UTC (rev 6423) +++ trunk/matplotlib/src/_backend_agg.cpp 2008-11-20 20:22:08 UTC (rev 6424) @@ -386,7 +386,7 @@ throw Py::TypeError("Invalid bbox provided to copy_from_bbox"); // std::cout << l << " " << b << " " << r << " " << t << " " << (height - (int)b) << " " << height - (int)t << std::endl; - agg::rect_i rect((int)l, height - (int)b, (int)r, height - (int)t); + agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b); BufferRegion* reg = NULL; try { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-21 11:28:35
|
Revision: 6429 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6429&view=rev Author: jdh2358 Date: 2008-11-21 11:28:32 +0000 (Fri, 21 Nov 2008) Log Message: ----------- Merged revisions 6086,6365,6427-6428 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_91_maint ........ r6086 | mdboom | 2008-09-11 15:28:11 -0500 (Thu, 11 Sep 2008) | 2 lines Fix backticks in PS output. ........ r6365 | mdboom | 2008-11-05 09:15:28 -0600 (Wed, 05 Nov 2008) | 1 line Fix bug in zoom rectangle with twin axes ........ r6427 | jdh2358 | 2008-11-21 05:14:12 -0600 (Fri, 21 Nov 2008) | 1 line fixed poly between ........ r6428 | jdh2358 | 2008-11-21 05:15:04 -0600 (Fri, 21 Nov 2008) | 1 line fixed poly below ........ Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/mlab.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-6073,6149 + /branches/v0_91_maint:1-6428 Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-21 11:15:04 UTC (rev 6428) +++ trunk/matplotlib/CHANGELOG 2008-11-21 11:28:32 UTC (rev 6429) @@ -54,6 +54,10 @@ 2008-10-08 Add path simplification support to paths with gaps. - EF +======= +2008-11-05 Fix bug with zoom to rectangle and twin axes - MGD + +>>>>>>> .merge-right.r6428 2008-10-05 Fix problem with AFM files that don't specify the font's full name or family name. - JKS @@ -97,6 +101,10 @@ 2008-09-10 Add "filled" kwarg to Path.intersects_path and Path.intersects_bbox. - MGD +======= +2008-09-11 Fix use of backticks in PS - MGD + +>>>>>>> .merge-right.r6086 2008-09-07 Changed full arrows slightly to avoid an xpdf rendering problem reported by Friedrich Hagedorn. - JKS Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-21 11:15:04 UTC (rev 6428) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-21 11:28:32 UTC (rev 6429) @@ -1883,6 +1883,8 @@ for cur_xypress in self._xypress: x, y = event.x, event.y lastx, lasty, a, ind, lim, trans = cur_xypress + if a._sharex or a._sharey: + continue # ignore singular clicks - 5 pixels is a threshold if abs(x-lastx)<5 or abs(y-lasty)<5: self._xypress = None Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-21 11:15:04 UTC (rev 6428) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-11-21 11:28:32 UTC (rev 6429) @@ -1438,6 +1438,181 @@ else: return X +def slopes(x,y): + """ + SLOPES calculate the slope y'(x) Given data vectors X and Y SLOPES + calculates Y'(X), i.e the slope of a curve Y(X). The slope is + estimated using the slope obtained from that of a parabola through + any three consecutive points. + + This method should be superior to that described in the appendix + of A CONSISTENTLY WELL BEHAVED METHOD OF INTERPOLATION by Russel + W. Stineman (Creative Computing July 1980) in at least one aspect: + + Circles for interpolation demand a known aspect ratio between x- + and y-values. For many functions, however, the abscissa are given + in different dimensions, so an aspect ratio is completely + arbitrary. + + The parabola method gives very similar results to the circle + method for most regular cases but behaves much better in special + cases + + Norbert Nemec, Institute of Theoretical Physics, University or + Regensburg, April 2006 Norbert.Nemec at physik.uni-regensburg.de + + (inspired by a original implementation by Halldor Bjornsson, + Icelandic Meteorological Office, March 2006 halldor at vedur.is) + """ + # Cast key variables as float. + x=np.asarray(x, np.float_) + y=np.asarray(y, np.float_) + + yp=np.zeros(y.shape, np.float_) + + dx=x[1:] - x[:-1] + dy=y[1:] - y[:-1] + dydx = dy/dx + yp[1:-1] = (dydx[:-1] * dx[1:] + dydx[1:] * dx[:-1])/(dx[1:] + dx[:-1]) + yp[0] = 2.0 * dy[0]/dx[0] - yp[1] + yp[-1] = 2.0 * dy[-1]/dx[-1] - yp[-2] + return yp + + +def stineman_interp(xi,x,y,yp=None): + """ + STINEMAN_INTERP Well behaved data interpolation. Given data + vectors X and Y, the slope vector YP and a new abscissa vector XI + the function stineman_interp(xi,x,y,yp) uses Stineman + interpolation to calculate a vector YI corresponding to XI. + + Here's an example that generates a coarse sine curve, then + interpolates over a finer abscissa: + + x = linspace(0,2*pi,20); y = sin(x); yp = cos(x) + xi = linspace(0,2*pi,40); + yi = stineman_interp(xi,x,y,yp); + plot(x,y,'o',xi,yi) + + The interpolation method is described in the article A + CONSISTENTLY WELL BEHAVED METHOD OF INTERPOLATION by Russell + W. Stineman. The article appeared in the July 1980 issue of + Creative Computing with a note from the editor stating that while + they were + + not an academic journal but once in a while something serious + and original comes in adding that this was + "apparently a real solution" to a well known problem. + + For yp=None, the routine automatically determines the slopes using + the "slopes" routine. + + X is assumed to be sorted in increasing order + + For values xi[j] < x[0] or xi[j] > x[-1], the routine tries a + extrapolation. The relevance of the data obtained from this, of + course, questionable... + + original implementation by Halldor Bjornsson, Icelandic + Meteorolocial Office, March 2006 halldor at vedur.is + + completely reworked and optimized for Python by Norbert Nemec, + Institute of Theoretical Physics, University or Regensburg, April + 2006 Norbert.Nemec at physik.uni-regensburg.de + + """ + + # Cast key variables as float. + x=np.asarray(x, np.float_) + y=np.asarray(y, np.float_) + assert x.shape == y.shape + N=len(y) + + if yp is None: + yp = slopes(x,y) + else: + yp=np.asarray(yp, np.float_) + + xi=np.asarray(xi, np.float_) + yi=np.zeros(xi.shape, np.float_) + + # calculate linear slopes + dx = x[1:] - x[:-1] + dy = y[1:] - y[:-1] + s = dy/dx #note length of s is N-1 so last element is #N-2 + + # find the segment each xi is in + # this line actually is the key to the efficiency of this implementation + idx = np.searchsorted(x[1:-1], xi) + + # now we have generally: x[idx[j]] <= xi[j] <= x[idx[j]+1] + # except at the boundaries, where it may be that xi[j] < x[0] or xi[j] > x[-1] + + # the y-values that would come out from a linear interpolation: + sidx = s.take(idx) + xidx = x.take(idx) + yidx = y.take(idx) + xidxp1 = x.take(idx+1) + yo = yidx + sidx * (xi - xidx) + + # the difference that comes when using the slopes given in yp + dy1 = (yp.take(idx)- sidx) * (xi - xidx) # using the yp slope of the left point + dy2 = (yp.take(idx+1)-sidx) * (xi - xidxp1) # using the yp slope of the right point + + dy1dy2 = dy1*dy2 + # The following is optimized for Python. The solution actually + # does more calculations than necessary but exploiting the power + # of numpy, this is far more efficient than coding a loop by hand + # in Python + yi = yo + dy1dy2 * np.choose(np.array(np.sign(dy1dy2), np.int32)+1, + ((2*xi-xidx-xidxp1)/((dy1-dy2)*(xidxp1-xidx)), + 0.0, + 1/(dy1+dy2),)) + return yi + +def inside_poly(points, verts): + """ + points is a sequence of x,y points + verts is a sequence of x,y vertices of a poygon + + return value is a sequence of indices into points for the points + that are inside the polygon + """ + res, = np.nonzero(nxutils.points_inside_poly(points, verts)) + return res + +def poly_below(ymin, xs, ys): + """ + given a arrays *xs* and *ys*, return the vertices of a polygon + that has a scalar lower bound *ymin* and an upper bound at the *ys*. + + intended for use with Axes.fill, eg:: + + xv, yv = poly_below(0, x, y) + ax.fill(xv, yv) + """ + return poly_between(xs, ys, xmin) + + +def poly_between(x, ylower, yupper): + """ + given a sequence of x, ylower and yupper, return the polygon that + fills the regions between them. ylower or yupper can be scalar or + iterable. If they are iterable, they must be equal in length to x + + return value is x, y arrays for use with Axes.fill + """ + Nx = len(x) + if not cbook.iterable(ylower): + ylower = ylower*np.ones(Nx) + + if not cbook.iterable(yupper): + yupper = yupper*np.ones(Nx) + + x = np.concatenate( (x, x[::-1]) ) + y = np.concatenate( (yupper, ylower[::-1]) ) + return x,y + ### the following code was written and submitted by Fernando Perez ### from the ipython numutils package under a BSD license # begin fperez functions This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-23 19:04:40
|
Revision: 6432 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6432&view=rev Author: jdh2358 Date: 2008-11-23 19:04:35 +0000 (Sun, 23 Nov 2008) Log Message: ----------- generalized fill between poly collection Modified Paths: -------------- trunk/matplotlib/examples/api/filled_masked_regions.py trunk/matplotlib/lib/matplotlib/collections.py Modified: trunk/matplotlib/examples/api/filled_masked_regions.py =================================================================== --- trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-21 18:20:00 UTC (rev 6431) +++ trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-23 19:04:35 UTC (rev 6432) @@ -8,31 +8,36 @@ t = np.arange(0.0, 2, 0.01) -s = np.sin(2*np.pi*t) +s1 = np.sin(2*np.pi*t) +s2 = 1.2*np.sin(4*np.pi*t) fig = plt.figure() ax = fig.add_subplot(111) -ax.set_title('using fill_between_masked') -ax.plot(t, s, '-') +ax.set_title('using fill_between_where') +ax.plot(t, s1, t, s2) ax.axhline(0, color='black', lw=2) -collection = collections.PolyCollection.fill_between_masked(t, s, s>=0, yboundary=0, color='green', alpha=0.5) +collection = collections.PolyCollection.fill_between_where( + t, s1, s2, s1>=s2, color='green', alpha=0.5) ax.add_collection(collection) -collection = collections.PolyCollection.fill_between_masked(t, s, s<=0, yboundary=0, color='red', alpha=0.5) +collection = collections.PolyCollection.fill_between_where( + t, s1, s2, s1<=s2, color='red', alpha=0.5) ax.add_collection(collection) fig = plt.figure() ax = fig.add_subplot(111) ax.set_title('using span_masked') -ax.plot(t, s, '-') +ax.plot(t, s1, '-') ax.axhline(0, color='black', lw=2) -collection = collections.BrokenBarHCollection.span_masked(t, s>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) +collection = collections.BrokenBarHCollection.span_masked( + t, s1>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) ax.add_collection(collection) -collection = collections.BrokenBarHCollection.span_masked(t, s<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) +collection = collections.BrokenBarHCollection.span_masked( + t, s1<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) ax.add_collection(collection) Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-21 18:20:00 UTC (rev 6431) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-23 19:04:35 UTC (rev 6432) @@ -674,7 +674,7 @@ @staticmethod - def fill_between_masked(x, y, mask, yboundary=0, **kwargs): + def fill_between_where(x, y1, y2, mask, **kwargs): """ Create a :class:`PolyCollection` filling the regions between *y* and *yboundary7* where ``mask==True`` @@ -683,35 +683,50 @@ *x* an N length np array of the x data - *y* - an N length np array of the y data + *y1* + an N length scalar or np array of the x data + *y2* + an N length scalar or np array of the x data + *mask* an N length numpy boolean array - *yboundary* - a scalar to fill between *y* and the boundary - *kwargs* keyword args passed on to the :class:`PolyCollection` """ + if not cbook.iterable(y1): + y1 = np.ones_like(x)*y1 + + if not cbook.iterable(y2): + y2 = np.ones_like(x)*y2 + + assert( (len(x)==len(y1)) and (len(x)==len(y2)) ) + polys = [] for ind0, ind1 in mlab.contiguous_regions(mask): theseverts = [] xslice = x[ind0:ind1] - yslice = y[ind0:ind1] + y1slice = y1[ind0:ind1] + y2slice = y2[ind0:ind1] + if not len(xslice): continue N = len(xslice) X = np.zeros((2*N+2, 2), np.float) - X[0] = xslice[0], yboundary - X[N+1] = xslice[-1], yboundary + + # the purpose of the next two lines is for when y2 is a + # scalar like 0 and we want the fill to go all the way + # down to 0 even if none of the y1 sample points do + X[0] = xslice[0], y2slice[0] + X[N+1] = xslice[-1], y2slice[-1] + X[1:N+1,0] = xslice - X[1:N+1,1] = yslice + X[1:N+1,1] = y1slice X[N+2:,0] = xslice[::-1] - X[N+2:,1] = yboundary + X[N+2:,1] = y2slice[::-1] polys.append(X) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-23 19:06:02
|
Revision: 6433 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6433&view=rev Author: jdh2358 Date: 2008-11-23 19:05:58 +0000 (Sun, 23 Nov 2008) Log Message: ----------- renamed fill where examples Modified Paths: -------------- trunk/matplotlib/CHANGELOG Added Paths: ----------- trunk/matplotlib/examples/api/fill_where_demo.py Removed Paths: ------------- trunk/matplotlib/examples/api/filled_masked_regions.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-23 19:04:35 UTC (rev 6432) +++ trunk/matplotlib/CHANGELOG 2008-11-23 19:05:58 UTC (rev 6433) @@ -1,10 +1,7 @@ 2008-11-20 Added some static helper methods BrokenHBarCollection.span_masked and - PolyCollection.fill_between_masked for visualizing - non-masked regions. In the longer term, the better - solution will be to fix the relevant classes and functions - to handle masked data, so this may be a temporary solution - - JDH + PolyCollection.fill_between_where for visualizing logical + regions. See examples/api/fill_where_demo.py - JDH 2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, and use them to determine whether either or both Copied: trunk/matplotlib/examples/api/fill_where_demo.py (from rev 6432, trunk/matplotlib/examples/api/filled_masked_regions.py) =================================================================== --- trunk/matplotlib/examples/api/fill_where_demo.py (rev 0) +++ trunk/matplotlib/examples/api/fill_where_demo.py 2008-11-23 19:05:58 UTC (rev 6433) @@ -0,0 +1,50 @@ +""" +Illustrate some helper functions for shading regions where a logical +mask is True +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.collections as collections + + +t = np.arange(0.0, 2, 0.01) +s1 = np.sin(2*np.pi*t) +s2 = 1.2*np.sin(4*np.pi*t) + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using fill_between_where') +ax.plot(t, s1, t, s2) +ax.axhline(0, color='black', lw=2) + +collection = collections.PolyCollection.fill_between_where( + t, s1, s2, s1>=s2, color='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.PolyCollection.fill_between_where( + t, s1, s2, s1<=s2, color='red', alpha=0.5) +ax.add_collection(collection) + + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using span_masked') +ax.plot(t, s1, '-') +ax.axhline(0, color='black', lw=2) + +collection = collections.BrokenBarHCollection.span_masked( + t, s1>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.BrokenBarHCollection.span_masked( + t, s1<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) +ax.add_collection(collection) + + + +plt.show() + + + + + Deleted: trunk/matplotlib/examples/api/filled_masked_regions.py =================================================================== --- trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-23 19:04:35 UTC (rev 6432) +++ trunk/matplotlib/examples/api/filled_masked_regions.py 2008-11-23 19:05:58 UTC (rev 6433) @@ -1,50 +0,0 @@ -""" -Illustrate some helper functions for shading regions where a logical -mask is True -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.collections as collections - - -t = np.arange(0.0, 2, 0.01) -s1 = np.sin(2*np.pi*t) -s2 = 1.2*np.sin(4*np.pi*t) - -fig = plt.figure() -ax = fig.add_subplot(111) -ax.set_title('using fill_between_where') -ax.plot(t, s1, t, s2) -ax.axhline(0, color='black', lw=2) - -collection = collections.PolyCollection.fill_between_where( - t, s1, s2, s1>=s2, color='green', alpha=0.5) -ax.add_collection(collection) - -collection = collections.PolyCollection.fill_between_where( - t, s1, s2, s1<=s2, color='red', alpha=0.5) -ax.add_collection(collection) - - -fig = plt.figure() -ax = fig.add_subplot(111) -ax.set_title('using span_masked') -ax.plot(t, s1, '-') -ax.axhline(0, color='black', lw=2) - -collection = collections.BrokenBarHCollection.span_masked( - t, s1>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) -ax.add_collection(collection) - -collection = collections.BrokenBarHCollection.span_masked( - t, s1<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) -ax.add_collection(collection) - - - -plt.show() - - - - - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-23 19:15:04
|
Revision: 6434 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6434&view=rev Author: jdh2358 Date: 2008-11-23 19:15:01 +0000 (Sun, 23 Nov 2008) Log Message: ----------- updated api for fill_between_where and span_where Modified Paths: -------------- trunk/matplotlib/examples/api/fill_where_demo.py trunk/matplotlib/lib/matplotlib/collections.py Modified: trunk/matplotlib/examples/api/fill_where_demo.py =================================================================== --- trunk/matplotlib/examples/api/fill_where_demo.py 2008-11-23 19:05:58 UTC (rev 6433) +++ trunk/matplotlib/examples/api/fill_where_demo.py 2008-11-23 19:15:01 UTC (rev 6434) @@ -1,6 +1,9 @@ """ Illustrate some helper functions for shading regions where a logical mask is True + +See :meth:`matplotlib.collections.PolyCollection.fill_between_where` +and :meth:`matplotlib.collections.BrokenBarHCollection.span_where` """ import numpy as np import matplotlib.pyplot as plt @@ -18,26 +21,26 @@ ax.axhline(0, color='black', lw=2) collection = collections.PolyCollection.fill_between_where( - t, s1, s2, s1>=s2, color='green', alpha=0.5) + t, s1, s2, where=s1>=s2, color='green', alpha=0.5) ax.add_collection(collection) collection = collections.PolyCollection.fill_between_where( - t, s1, s2, s1<=s2, color='red', alpha=0.5) + t, s1, s2, where=s1<=s2, color='red', alpha=0.5) ax.add_collection(collection) fig = plt.figure() ax = fig.add_subplot(111) -ax.set_title('using span_masked') +ax.set_title('using span_where') ax.plot(t, s1, '-') ax.axhline(0, color='black', lw=2) -collection = collections.BrokenBarHCollection.span_masked( - t, s1>0, ymin=0, ymax=1, facecolor='green', alpha=0.5) +collection = collections.BrokenBarHCollection.span_where( + t, ymin=0, ymax=1, where=s1>0, facecolor='green', alpha=0.5) ax.add_collection(collection) -collection = collections.BrokenBarHCollection.span_masked( - t, s1<0, ymin=-1, ymax=0, facecolor='red', alpha=0.5) +collection = collections.BrokenBarHCollection.span_where( + t, ymin=-1, ymax=0, where=s1<0, facecolor='red', alpha=0.5) ax.add_collection(collection) Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-23 19:05:58 UTC (rev 6433) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-23 19:15:01 UTC (rev 6434) @@ -674,10 +674,10 @@ @staticmethod - def fill_between_where(x, y1, y2, mask, **kwargs): + def fill_between_where(x, y1, y2, where, **kwargs): """ Create a :class:`PolyCollection` filling the regions between *y* - and *yboundary7* where ``mask==True`` + and *yboundary7* where ``where==True`` *x* @@ -689,7 +689,7 @@ *y2* an N length scalar or np array of the x data - *mask* + *where* an N length numpy boolean array *kwargs* @@ -705,7 +705,7 @@ assert( (len(x)==len(y1)) and (len(x)==len(y2)) ) polys = [] - for ind0, ind1 in mlab.contiguous_regions(mask): + for ind0, ind1 in mlab.contiguous_regions(where): theseverts = [] xslice = x[ind0:ind1] y1slice = y1[ind0:ind1] @@ -756,17 +756,17 @@ @staticmethod - def span_masked(x, mask, ymin, ymax, **kwargs): + def span_where(x, ymin, ymax, where, **kwargs): """ Create a BrokenBarHCollection to plot horizontal bars from - over the regions in *x* where *mask* is True. The bars range + over the regions in *x* where *where* is True. The bars range on the y-axis from *ymin* to *ymax* A :class:`BrokenBarHCollection` is returned. **kwargs are passed on to the collection """ xranges = [] - for ind0, ind1 in mlab.contiguous_regions(mask): + for ind0, ind1 in mlab.contiguous_regions(where): xslice = x[ind0:ind1] if not len(xslice): continue This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-23 19:56:41
|
Revision: 6437 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6437&view=rev Author: jdh2358 Date: 2008-11-23 19:56:37 +0000 (Sun, 23 Nov 2008) Log Message: ----------- moved fill_between to axes/pyplot method Modified Paths: -------------- trunk/matplotlib/boilerplate.py trunk/matplotlib/doc/_templates/index.html trunk/matplotlib/examples/api/fill_where_demo.py trunk/matplotlib/examples/pylab_examples/fill_between.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/lib/matplotlib/pyplot.py Added Paths: ----------- trunk/matplotlib/examples/api/span_regions.py Modified: trunk/matplotlib/boilerplate.py =================================================================== --- trunk/matplotlib/boilerplate.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/boilerplate.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -64,6 +64,7 @@ 'csd', 'errorbar', 'fill', + 'fill_between', 'hexbin', 'hist', 'hlines', Modified: trunk/matplotlib/doc/_templates/index.html =================================================================== --- trunk/matplotlib/doc/_templates/index.html 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/doc/_templates/index.html 2008-11-23 19:56:37 UTC (rev 6437) @@ -409,8 +409,21 @@ </td> </tr> + <tr> <th align="left"> + <a href="api/pyplot_api.html#matplotlib.pyplot.fill_between">fill_between</a> + + </th> + + <td align="left"> + make filled polygons between two curves + </td> + + </tr> + + <tr> + <th align="left"> <a href="api/pyplot_api.html#matplotlib.pyplot.findobj">findobj</a> </th> Modified: trunk/matplotlib/examples/api/fill_where_demo.py =================================================================== --- trunk/matplotlib/examples/api/fill_where_demo.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/examples/api/fill_where_demo.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -17,7 +17,7 @@ fig = plt.figure() ax = fig.add_subplot(111) ax.set_title('using fill_between_where') -ax.plot(t, s1, t, s2) +ax.plot(t, s1, t, s2, color='black') ax.axhline(0, color='black', lw=2) collection = collections.PolyCollection.fill_between_where( @@ -32,7 +32,7 @@ fig = plt.figure() ax = fig.add_subplot(111) ax.set_title('using span_where') -ax.plot(t, s1, '-') +ax.plot(t, s1, , color='black') ax.axhline(0, color='black', lw=2) collection = collections.BrokenBarHCollection.span_where( Added: trunk/matplotlib/examples/api/span_regions.py =================================================================== --- trunk/matplotlib/examples/api/span_regions.py (rev 0) +++ trunk/matplotlib/examples/api/span_regions.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -0,0 +1,38 @@ +""" +Illustrate some helper functions for shading regions where a logical +mask is True + +See :meth:`matplotlib.collections.BrokenBarHCollection.span_where` +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.collections as collections + + +t = np.arange(0.0, 2, 0.01) +s1 = np.sin(2*np.pi*t) +s2 = 1.2*np.sin(4*np.pi*t) + + +fig = plt.figure() +ax = fig.add_subplot(111) +ax.set_title('using span_where') +ax.plot(t, s1, color='black') +ax.axhline(0, color='black', lw=2) + +collection = collections.BrokenBarHCollection.span_where( + t, ymin=0, ymax=1, where=s1>0, facecolor='green', alpha=0.5) +ax.add_collection(collection) + +collection = collections.BrokenBarHCollection.span_where( + t, ymin=-1, ymax=0, where=s1<0, facecolor='red', alpha=0.5) +ax.add_collection(collection) + + + +plt.show() + + + + + Modified: trunk/matplotlib/examples/pylab_examples/fill_between.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fill_between.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/examples/pylab_examples/fill_between.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -3,27 +3,31 @@ from pylab import figure, show import numpy as np -x = np.arange(0, 2, 0.01) +x = np.arange(0.0, 2, 0.01) y1 = np.sin(2*np.pi*x) -y2 = np.sin(4*np.pi*x) + 2 +y2 = 1.2*np.sin(4*np.pi*x) fig = figure() -ax = fig.add_subplot(311) -ax2 = fig.add_subplot(312) -ax3 = fig.add_subplot(313) +ax1 = fig.add_subplot(311) +ax2 = fig.add_subplot(312, sharex=ax1) +ax3 = fig.add_subplot(313, sharex=ax1) +ax1.fill_between(x, 0, y1) +ax1.set_ylabel('between y1 and 0') -xs, ys = mlab.poly_between(x, 0, y1) -ax.fill(xs, ys) -ax.set_ylabel('between y1 and 0') - -xs, ys = mlab.poly_between(x, y1, 1) -ax2.fill(xs, ys) +ax2.fill_between(x, y1, 1) ax2.set_ylabel('between y1 and 1') -xs, ys = mlab.poly_between(x, y1, y2) -ax3.fill(xs, ys) +ax3.fill_between(x, y1, y2) ax3.set_ylabel('between y1 and y2') ax3.set_xlabel('x') + +fig = figure() +ax = fig.add_subplot(111) +ax1.plot(x, y1, x, y2, color='black') +ax.fill_between(x, y1, y2, where=y2>y1, facecolor='green') +ax.fill_between(x, y1, y2, where=y2<=y1, facecolor='red') +ax.set_title('fill between where') + show() Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -1302,6 +1302,7 @@ if autolim: if collection._paths and len(collection._paths): self.update_datalim(collection.get_datalim(self.transData)) + collection._remove_method = lambda h: self.collections.remove(h) def add_line(self, line): @@ -5456,12 +5457,8 @@ supports are supported by the fill format string. If you would like to fill below a curve, eg. shade a region - between 0 and *y* along *x*, use - :func:`~matplotlib.pylab.poly_between`, eg.:: + between 0 and *y* along *x*, use :meth:`fill_between` - xs, ys = poly_between(x, 0, y) - axes.fill(xs, ys, facecolor='red', alpha=0.5) - The *closed* kwarg will close the polygon when *True* (default). kwargs control the Polygon properties: @@ -5472,9 +5469,6 @@ .. plot:: mpl_examples/pylab_examples/fill_demo.py - .. seealso:: - :file:`examples/pylab_examples/fill_between.py`: - For more examples. """ if not self._hold: self.cla() @@ -5486,6 +5480,92 @@ return patches fill.__doc__ = cbook.dedent(fill.__doc__) % martist.kwdocd + def fill_between(self, x, y1, y2=0, where=None, **kwargs): + """ + call signature:: + + fill_between(x, y1, y2=0, where=None, **kwargs) + + Create a :class:`~matplotlib.collectionsPolyCollection` + filling the regions between *y1* and *y2* where + ``where==True`` + + *x* + an N length np array of the x data + + *y1* + an N length scalar or np array of the x data + + *y2* + an N length scalar or np array of the x data + + *where* + if None, default to fill between everywhere. If not None, + it is a a N length numpy boolean array and the fill will + only happen over the regions where ``where==True`` + + *kwargs* + keyword args passed on to the :class:`PolyCollection` + + .. seealso:: + :file:`examples/pylab_examples/fill_between.py`: + For more examples. + + kwargs control the Polygon properties: + + %(PolyCollection)s + + """ + x = np.asarray(x) + if not cbook.iterable(y1): + y1 = np.ones_like(x)*y1 + + if not cbook.iterable(y2): + y2 = np.ones_like(x)*y2 + + if where is None: + where = np.ones(len(x), np.bool) + + y1 = np.asarray(y1) + y2 = np.asarray(y2) + where = np.asarray(where) + assert( (len(x)==len(y1)) and (len(x)==len(y2)) and len(x)==len(where)) + + polys = [] + for ind0, ind1 in mlab.contiguous_regions(where): + theseverts = [] + xslice = x[ind0:ind1] + y1slice = y1[ind0:ind1] + y2slice = y2[ind0:ind1] + + if not len(xslice): + continue + + N = len(xslice) + X = np.zeros((2*N+2, 2), np.float) + + # the purpose of the next two lines is for when y2 is a + # scalar like 0 and we want the fill to go all the way + # down to 0 even if none of the y1 sample points do + X[0] = xslice[0], y2slice[0] + X[N+1] = xslice[-1], y2slice[-1] + + X[1:N+1,0] = xslice + X[1:N+1,1] = y1slice + X[N+2:,0] = xslice[::-1] + X[N+2:,1] = y2slice[::-1] + + polys.append(X) + + collection = mcoll.PolyCollection(polys, **kwargs) + + self.update_datalim_numerix(x[where], y1[where]) + self.update_datalim_numerix(x[where], y2[where]) + self.add_collection(collection) + self.autoscale_view() + return collection + fill_between.__doc__ = cbook.dedent(fill_between.__doc__) % martist.kwdocd + #### plotting z(x,y): imshow, pcolor and relatives, contour def imshow(self, X, cmap=None, norm=None, aspect=None, Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -673,66 +673,6 @@ return Collection.draw(self, renderer) - @staticmethod - def fill_between_where(x, y1, y2, where, **kwargs): - """ - Create a :class:`PolyCollection` filling the regions between *y* - and *yboundary7* where ``where==True`` - - - *x* - an N length np array of the x data - - *y1* - an N length scalar or np array of the x data - - *y2* - an N length scalar or np array of the x data - - *where* - an N length numpy boolean array - - *kwargs* - keyword args passed on to the :class:`PolyCollection` - - """ - if not cbook.iterable(y1): - y1 = np.ones_like(x)*y1 - - if not cbook.iterable(y2): - y2 = np.ones_like(x)*y2 - - assert( (len(x)==len(y1)) and (len(x)==len(y2)) ) - - polys = [] - for ind0, ind1 in mlab.contiguous_regions(where): - theseverts = [] - xslice = x[ind0:ind1] - y1slice = y1[ind0:ind1] - y2slice = y2[ind0:ind1] - - if not len(xslice): - continue - - N = len(xslice) - X = np.zeros((2*N+2, 2), np.float) - - # the purpose of the next two lines is for when y2 is a - # scalar like 0 and we want the fill to go all the way - # down to 0 even if none of the y1 sample points do - X[0] = xslice[0], y2slice[0] - X[N+1] = xslice[-1], y2slice[-1] - - X[1:N+1,0] = xslice - X[1:N+1,1] = y1slice - X[N+2:,0] = xslice[::-1] - X[N+2:,1] = y2slice[::-1] - - polys.append(X) - - collection = PolyCollection(polys, **kwargs) - return collection - class BrokenBarHCollection(PolyCollection): """ A collection of horizontal bars spanning *yrange* with a sequence of Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-11-23 19:18:24 UTC (rev 6436) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-11-23 19:56:37 UTC (rev 6437) @@ -1120,61 +1120,62 @@ """ Plotting commands - ========= ================================================= - Command Description - ========= ================================================= - axes Create a new axes - axis Set or return the current axis limits - bar make a bar chart - boxplot make a box and whiskers chart - cla clear current axes - clabel label a contour plot - clf clear a figure window - close close a figure window - colorbar add a colorbar to the current figure - cohere make a plot of coherence - contour make a contour plot - contourf make a filled contour plot - csd make a plot of cross spectral density - draw force a redraw of the current figure - errorbar make an errorbar graph - figlegend add a legend to the figure - figimage add an image to the figure, w/o resampling - figtext add text in figure coords - figure create or change active figure - fill make filled polygons - gca return the current axes - gcf return the current figure - gci get the current image, or None - getp get a handle graphics property - hist make a histogram - hold set the hold state on current axes - legend add a legend to the axes - loglog a log log plot - imread load image file into array - imshow plot image data - matshow display a matrix in a new figure preserving aspect - pcolor make a pseudocolor plot - plot make a line plot - plotfile plot data from a flat file - psd make a plot of power spectral density - quiver make a direction field (arrows) plot - rc control the default params - savefig save the current figure - scatter make a scatter plot - setp set a handle graphics property - semilogx log x axis - semilogy log y axis - show show the figures - specgram a spectrogram plot - stem make a stem plot - subplot make a subplot (numrows, numcols, axesnum) - table add a table to the axes - text add some text at location x,y to the current axes - title add a title to the current axes - xlabel add an xlabel to the current axes - ylabel add a ylabel to the current axes - ========= ================================================= + ========= ================================================= + Command Description + ========= ================================================= + axes Create a new axes + axis Set or return the current axis limits + bar make a bar chart + boxplot make a box and whiskers chart + cla clear current axes + clabel label a contour plot + clf clear a figure window + close close a figure window + colorbar add a colorbar to the current figure + cohere make a plot of coherence + contour make a contour plot + contourf make a filled contour plot + csd make a plot of cross spectral density + draw force a redraw of the current figure + errorbar make an errorbar graph + figlegend add a legend to the figure + figimage add an image to the figure, w/o resampling + figtext add text in figure coords + figure create or change active figure + fill make filled polygons + fill_between make filled polygons + gca return the current axes + gcf return the current figure + gci get the current image, or None + getp get a handle graphics property + hist make a histogram + hold set the hold state on current axes + legend add a legend to the axes + loglog a log log plot + imread load image file into array + imshow plot image data + matshow display a matrix in a new figure preserving aspect + pcolor make a pseudocolor plot + plot make a line plot + plotfile plot data from a flat file + psd make a plot of power spectral density + quiver make a direction field (arrows) plot + rc control the default params + savefig save the current figure + scatter make a scatter plot + setp set a handle graphics property + semilogx log x axis + semilogy log y axis + show show the figures + specgram a spectrogram plot + stem make a stem plot + subplot make a subplot (numrows, numcols, axesnum) + table add a table to the axes + text add some text at location x,y to the current axes + title add a title to the current axes + xlabel add an xlabel to the current axes + ylabel add a ylabel to the current axes + ========= ================================================= The following commands will set the default colormap accordingly: @@ -1493,7 +1494,6 @@ ## Plotting part 2: autogenerated wrappers for axes methods ## -### Do not edit below this point # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost def acorr(*args, **kwargs): @@ -1870,6 +1870,50 @@ # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost +def fill_between(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = ishold() + h = kwargs.pop('hold', None) + if h is not None: + hold(h) + try: + ret = gca().fill_between(*args, **kwargs) + draw_if_interactive() + except: + hold(b) + raise + + hold(b) + return ret +if Axes.fill_between.__doc__ is not None: + fill_between.__doc__ = dedent(Axes.fill_between.__doc__) + """ + +Additional kwargs: hold = [True|False] overrides default hold state""" + +# This function was autogenerated by boilerplate.py. Do not edit as +# changes will be lost +def hexbin(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = ishold() + h = kwargs.pop('hold', None) + if h is not None: + hold(h) + try: + ret = gca().hexbin(*args, **kwargs) + draw_if_interactive() + except: + hold(b) + raise + gci._current = ret + hold(b) + return ret +if Axes.hexbin.__doc__ is not None: + hexbin.__doc__ = dedent(Axes.hexbin.__doc__) + """ + +Additional kwargs: hold = [True|False] overrides default hold state""" + +# This function was autogenerated by boilerplate.py. Do not edit as +# changes will be lost def hist(*args, **kwargs): # allow callers to override the hold state by passing hold=True|False b = ishold() @@ -2156,28 +2200,6 @@ # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost -def hexbin(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = ishold() - h = kwargs.pop('hold', None) - if h is not None: - hold(h) - try: - ret = gca().hexbin(*args, **kwargs) - draw_if_interactive() - except: - hold(b) - raise - gci._current = ret - hold(b) - return ret -if Axes.hexbin.__doc__ is not None: - hexbin.__doc__ = dedent(Axes.hexbin.__doc__) + """ - -Additional kwargs: hold = [True|False] overrides default hold state""" - -# This function was autogenerated by boilerplate.py. Do not edit as -# changes will be lost def semilogx(*args, **kwargs): # allow callers to override the hold state by passing hold=True|False b = ishold() @@ -2438,10 +2460,8 @@ # changes will be lost def autumn(): ''' - Set the default colormap to *autumn* and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to autumn and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='autumn') im = gci() @@ -2455,10 +2475,8 @@ # changes will be lost def bone(): ''' - Set the default colormap to bone and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to bone and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='bone') im = gci() @@ -2472,10 +2490,8 @@ # changes will be lost def cool(): ''' - Set the default colormap to cool and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to cool and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='cool') im = gci() @@ -2489,10 +2505,8 @@ # changes will be lost def copper(): ''' - Set the default colormap to copper and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to copper and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='copper') im = gci() @@ -2506,10 +2520,8 @@ # changes will be lost def flag(): ''' - Set the default colormap to flag and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to flag and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='flag') im = gci() @@ -2523,10 +2535,8 @@ # changes will be lost def gray(): ''' - Set the default colormap to gray and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to gray and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='gray') im = gci() @@ -2540,10 +2550,8 @@ # changes will be lost def hot(): ''' - Set the default colormap to hot and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to hot and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='hot') im = gci() @@ -2557,10 +2565,8 @@ # changes will be lost def hsv(): ''' - Set the default colormap to hsv and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to hsv and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='hsv') im = gci() @@ -2574,10 +2580,8 @@ # changes will be lost def jet(): ''' - Set the default colormap to jet and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to jet and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='jet') im = gci() @@ -2591,10 +2595,8 @@ # changes will be lost def pink(): ''' - Set the default colormap to pink and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to pink and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='pink') im = gci() @@ -2608,10 +2610,8 @@ # changes will be lost def prism(): ''' - Set the default colormap to prism and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to prism and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='prism') im = gci() @@ -2625,10 +2625,8 @@ # changes will be lost def spring(): ''' - Set the default colormap to spring and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to spring and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='spring') im = gci() @@ -2642,10 +2640,8 @@ # changes will be lost def summer(): ''' - Set the default colormap to summer and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to summer and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='summer') im = gci() @@ -2659,10 +2655,8 @@ # changes will be lost def winter(): ''' - Set the default colormap to winter and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to winter and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='winter') im = gci() @@ -2676,10 +2670,8 @@ # changes will be lost def spectral(): ''' - Set the default colormap to spectral and apply to current image if any. - - .. seealso:: - :func:`colormaps` + set the default colormap to spectral and apply to current image if any. + See help(colormaps) for more information ''' rc('image', cmap='spectral') im = gci() @@ -2687,3 +2679,5 @@ if im is not None: im.set_cmap(cm.spectral) draw_if_interactive() + + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-11-24 20:12:59
|
Revision: 6442 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6442&view=rev Author: mdboom Date: 2008-11-24 20:12:55 +0000 (Mon, 24 Nov 2008) Log Message: ----------- [ 2314869 ] Log base-2 axes fail with certain axis limits Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/ticker.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-24 03:53:14 UTC (rev 6441) +++ trunk/matplotlib/CHANGELOG 2008-11-24 20:12:55 UTC (rev 6442) @@ -1,3 +1,5 @@ +2008-11-24 Fix crash in log ticking. - MGD + 2008-11-20 Added some static helper methods BrokenHBarCollection.span_masked and PolyCollection.fill_between_where for visualizing logical Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2008-11-24 03:53:14 UTC (rev 6441) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2008-11-24 20:12:55 UTC (rev 6442) @@ -1037,7 +1037,7 @@ decades = np.arange(math.floor(vmin), math.ceil(vmax)+stride, stride) - if len(subs) > 1 or subs[0] != 1.0: + if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): ticklocs = [] for decadeStart in b**decades: ticklocs.extend( subs*decadeStart ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-25 04:04:05
|
Revision: 6445 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6445&view=rev Author: jdh2358 Date: 2008-11-25 04:03:59 +0000 (Tue, 25 Nov 2008) Log Message: ----------- point to the right url for the rc file Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/doc/_templates/gallery.html trunk/matplotlib/doc/_templates/indexsidebar.html trunk/matplotlib/doc/make.py trunk/matplotlib/doc/users/customizing.rst trunk/matplotlib/lib/matplotlib/__init__.py trunk/matplotlib/matplotlibrc.template Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/CHANGELOG 2008-11-25 04:03:59 UTC (rev 6445) @@ -1,9 +1,8 @@ 2008-11-24 Fix crash in log ticking. - MGD -2008-11-20 Added some static helper methods - BrokenHBarCollection.span_masked and - PolyCollection.fill_between_where for visualizing logical - regions. See examples/api/fill_where_demo.py - JDH +2008-11-20 Added static helper method BrokenHBarCollection.span_where + and Axes/pyplot method fill_between. See + examples/pylab/fill_between.py - JDH 2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, and use them to determine whether either or both Modified: trunk/matplotlib/doc/_templates/gallery.html =================================================================== --- trunk/matplotlib/doc/_templates/gallery.html 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/doc/_templates/gallery.html 2008-11-25 04:03:59 UTC (rev 6445) @@ -49,6 +49,8 @@ <a href="examples/api/scatter_piecharts.html"><img src="_static/plot_directive/mpl_examples/api/thumbnails/scatter_piecharts.png" border="0" alt="scatter_piecharts"/></a> +<a href="examples/api/span_regions.html"><img src="_static/plot_directive/mpl_examples/api/thumbnails/span_regions.png" border="0" alt="span_regions"/></a> + <a href="examples/api/two_scales.html"><img src="_static/plot_directive/mpl_examples/api/thumbnails/two_scales.png" border="0" alt="two_scales"/></a> <a href="examples/api/watermark_image.html"><img src="_static/plot_directive/mpl_examples/api/thumbnails/watermark_image.png" border="0" alt="watermark_image"/></a> @@ -157,6 +159,8 @@ <a href="examples/pylab_examples/csd_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/csd_demo.png" border="0" alt="csd_demo"/></a> +<a href="examples/pylab_examples/custom_cmap.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/custom_cmap.png" border="0" alt="custom_cmap"/></a> + <a href="examples/pylab_examples/custom_figure_class.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/custom_figure_class.png" border="0" alt="custom_figure_class"/></a> <a href="examples/pylab_examples/custom_ticker1.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/custom_ticker1.png" border="0" alt="custom_ticker1"/></a> @@ -223,9 +227,9 @@ <a href="examples/pylab_examples/figure_title.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/figure_title.png" border="0" alt="figure_title"/></a> -<a href="examples/pylab_examples/fill_between.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/fill_between.png" border="0" alt="fill_between"/></a> +<a href="examples/pylab_examples/fill_between.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/fill_between_00.png" border="0" alt="fill_between"/></a> -<a href="examples/pylab_examples/fill_between_posneg.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/fill_between_posneg.png" border="0" alt="fill_between_posneg"/></a> +<a href="examples/pylab_examples/fill_between.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/fill_between_01.png" border="0" alt="fill_between"/></a> <a href="examples/pylab_examples/fill_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/fill_demo.png" border="0" alt="fill_demo"/></a> @@ -419,6 +423,8 @@ <a href="examples/pylab_examples/psd_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/psd_demo.png" border="0" alt="psd_demo"/></a> +<a href="examples/pylab_examples/psd_demo2.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/psd_demo2.png" border="0" alt="psd_demo2"/></a> + <a href="examples/pylab_examples/pythonic_matplotlib.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/pythonic_matplotlib.png" border="0" alt="pythonic_matplotlib"/></a> <a href="examples/pylab_examples/quadmesh_demo.html"><img src="_static/plot_directive/mpl_examples/pylab_examples/thumbnails/quadmesh_demo.png" border="0" alt="quadmesh_demo"/></a> Modified: trunk/matplotlib/doc/_templates/indexsidebar.html =================================================================== --- trunk/matplotlib/doc/_templates/indexsidebar.html 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/doc/_templates/indexsidebar.html 2008-11-25 04:03:59 UTC (rev 6445) @@ -33,7 +33,7 @@ <p>For details on what's new, see the detailed <a href="{{ pathto('_static/CHANGELOG', 1) }}">changelog</a>. Anything that could -required changes to your existing codes is logged in the <a href="{{ +require changes to your existing codes is logged in the <a href="{{ pathto('api/api_changes.html', 1) }}">api changes</a> file.</p> <h3>Other stuff</h3> Modified: trunk/matplotlib/doc/make.py =================================================================== --- trunk/matplotlib/doc/make.py 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/doc/make.py 2008-11-25 04:03:59 UTC (rev 6445) @@ -17,9 +17,6 @@ def sf(): 'push a copy to the sf site' shutil.copy('../CHANGELOG', 'build/html/_static/CHANGELOG') - shutil.copy('../API_CHANGES', 'build/html/_static/API_CHANGES') - shutil.copy('../MIGRATION.txt', 'build/html/_static/MIGRATION.txt') - os.system('cd build/html; rsync -avz . jdh2358,mat...@we...:/home/groups/m/ma/matplotlib/htdocs/ -essh --cvs-exclude') def sfpdf(): @@ -44,6 +41,7 @@ check_build() if not os.path.exists('examples/index.rst'): examples() + shutil.copy('mpl_data/matplotlibrc', '_static/matplotlibrc') #figs() if os.system('sphinx-build -b html -d build/doctrees . build/html'): raise SystemExit("Building HTML failed.") Modified: trunk/matplotlib/doc/users/customizing.rst =================================================================== --- trunk/matplotlib/doc/users/customizing.rst 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/doc/users/customizing.rst 2008-11-25 04:03:59 UTC (rev 6445) @@ -61,6 +61,10 @@ .. _matplotlibrc-sample: A sample matplotlibrc file --------------------------- +-------------------------------------------------------------------- +.. htmlonly:: + + `(download) <../_static/matplotlibrc>`__ + .. literalinclude:: ../mpl_data/matplotlibrc Modified: trunk/matplotlib/lib/matplotlib/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/__init__.py 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/lib/matplotlib/__init__.py 2008-11-25 04:03:59 UTC (rev 6445) @@ -666,7 +666,7 @@ Bad key "%s" on line %d in %s. You probably need to get an updated matplotlibrc file from -http://matplotlib.sf.net/matplotlibrc or from the matplotlib source +http://matplotlib.sf.net/_static/matplotlibrc or from the matplotlib source distribution""" % (key, cnt, fname) if ret['datapath'] is None: Modified: trunk/matplotlib/matplotlibrc.template =================================================================== --- trunk/matplotlib/matplotlibrc.template 2008-11-24 20:18:38 UTC (rev 6444) +++ trunk/matplotlib/matplotlibrc.template 2008-11-25 04:03:59 UTC (rev 6445) @@ -10,13 +10,16 @@ # (win32 systems). # # This file is best viewed in a editor which supports python mode -# syntax highlighting # Blank lines, or lines starting with a comment +# syntax highlighting. Blank lines, or lines starting with a comment # symbol, are ignored, as are trailing comments. Other lines must -# have the format # key : val # optional comment # Colors: for the -# color values below, you can either use - a matplotlib color string, -# such as r, k, or b - an rgb tuple, such as (1.0, 0.5, 0.0) - a hex -# string, such as ff00ff or #ff00ff - a scalar grayscale intensity -# such as 0.75 - a legal html color name, eg red, blue, darkslategray +# have the format +# key : val # optional comment +# +# Colors: for the color values below, you can either use - a +# matplotlib color string, such as r, k, or b - an rgb tuple, such as +# (1.0, 0.5, 0.0) - a hex string, such as ff00ff or #ff00ff - a scalar +# grayscale intensity such as 0.75 - a legal html color name, eg red, +# blue, darkslategray #### CONFIGURATION BEGINS HERE This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-11-25 18:57:02
|
Revision: 6448 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6448&view=rev Author: leejjoon Date: 2008-11-25 18:56:57 +0000 (Tue, 25 Nov 2008) Log Message: ----------- scatterpoints support in Legend. patch by Erik Tollerud Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/legend.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-25 18:28:13 UTC (rev 6447) +++ trunk/matplotlib/CHANGELOG 2008-11-25 18:56:57 UTC (rev 6448) @@ -1,3 +1,6 @@ +2008-11-25 Added scatterpoints support in Legend. patch by Erik + Tollerud - JJL + 2008-11-24 Fix crash in log ticking. - MGD 2008-11-20 Added static helper method BrokenHBarCollection.span_where Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-11-25 18:28:13 UTC (rev 6447) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-11-25 18:56:57 UTC (rev 6448) @@ -83,6 +83,7 @@ def __init__(self, parent, handles, labels, loc = None, numpoints = None, # the number of points in the legend line + scatterpoints = 3, # TODO: may be an rcParam prop = None, pad = None, # the fractional whitespace inside the legend border borderpad = None, @@ -101,6 +102,7 @@ labels # a list of strings to label the legend loc # a location code numpoints = 4 # the number of points in the legend line + scatterpoints = 3 # the number of points for the scatterplot legend prop = FontProperties(size='smaller') # the font property pad = 0.2 # the fractional whitespace inside the legend border markerscale = 0.6 # the relative size of legend markers vs. original @@ -118,10 +120,10 @@ Artist.__init__(self) - proplist=[numpoints, pad, borderpad, markerscale, labelsep, + proplist=[numpoints, scatterpoints, pad, borderpad, markerscale, labelsep, handlelen, handletextsep, axespad, shadow] - propnames=['numpoints', 'pad', 'borderpad', 'markerscale', 'labelsep', - 'handlelen', 'handletextsep', 'axespad', 'shadow'] + propnames=['numpoints','scatterpoints', 'pad', 'borderpad', 'markerscale', + 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow'] for name, value in safezip(propnames,proplist): if value is None: value=rcParams["legend."+name] @@ -130,7 +132,9 @@ warnings.warn("Use 'borderpad' instead of 'pad'.", DeprecationWarning) # 2008/10/04 if self.numpoints <= 0: - raise ValueError("numpoints must be >= 0; it was %d"% numpoints) + raise ValueError("numpoints must be > 0; it was %d"% numpoints) + if self.scatterpoints <= 0: + raise ValueError("scatterpoints must be > 0; it was %d"% numpoints) if prop is None: self.prop=FontProperties(size=rcParams["legend.fontsize"]) else: @@ -142,8 +146,8 @@ self._scatteryoffsets = np.array([4./8., 5./8., 3./8.]) else: self._scatteryoffsets = np.asarray(scatteryoffsets) - reps = int(self.numpoints / len(self._scatteryoffsets)) + 1 - self._scatteryoffsets = np.tile(self._scatteryoffsets, reps)[:self.numpoints] + reps = int(self.scatterpoints / len(self._scatteryoffsets)) + 1 + self._scatteryoffsets = np.tile(self._scatteryoffsets, reps)[:self.scatterpoints] if isinstance(parent,Axes): self.isaxes = True @@ -261,10 +265,14 @@ # centered marker proxy for handle, label in safezip(handles, texts): - if self.numpoints > 1: - xdata = np.linspace(left, left + self.handlelen, self.numpoints) + if isinstance(handle, RegularPolyCollection): + npoints = self.scatterpoints + else: + npoints = self.numpoints + if npoints > 1: + xdata = np.linspace(left, left + self.handlelen, npoints) xdata_marker = xdata - elif self.numpoints == 1: + elif npoints == 1: xdata = np.linspace(left, left + self.handlelen, 2) xdata_marker = [left + 0.5*self.handlelen] @@ -326,8 +334,11 @@ # we may need to scale these sizes by "markerscale" # attribute. But other handle types does not seem # to care about this attribute and it is currently ignored. - sizes = [.5*(size_max+size_min), size_max, - size_min] + if self.scatterpoints < 4: + sizes = [.5*(size_max+size_min), size_max, + size_min] + else: + sizes = size_max*np.linspace(0,1,self.scatterpoints)+size_min p = type(handle)(handle.get_numsides(), rotation=handle.get_rotation(), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-25 22:22:52
|
Revision: 6453 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6453&view=rev Author: jdh2358 Date: 2008-11-25 21:43:36 +0000 (Tue, 25 Nov 2008) Log Message: ----------- added rc param axes.unicode_minus Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/projections/polar.py trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/lib/matplotlib/ticker.py trunk/matplotlib/matplotlibrc.template Added Paths: ----------- trunk/matplotlib/examples/api/unicode_minus.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-25 21:14:13 UTC (rev 6452) +++ trunk/matplotlib/CHANGELOG 2008-11-25 21:43:36 UTC (rev 6453) @@ -1,3 +1,6 @@ +2008-11-25 Added rcParam axes.unicode_minus which allows plain hypen + for minus when False - JDH + 2008-11-25 Added scatterpoints support in Legend. patch by Erik Tollerud - JJL Added: trunk/matplotlib/examples/api/unicode_minus.py =================================================================== --- trunk/matplotlib/examples/api/unicode_minus.py (rev 0) +++ trunk/matplotlib/examples/api/unicode_minus.py 2008-11-25 21:43:36 UTC (rev 6453) @@ -0,0 +1,18 @@ +""" +You can use the proper typesetting unicode minus (see +http://en.wikipedia.org/wiki/Plus_sign#Plus_sign) or the ASCII hypen +for minus, which some people prefer. The matplotlibrc param +axes.unicode_minus controls the default behavior. + +The default is to use the unicode minus +""" +import numpy as np +import matplotlib +import matplotlib.pyplot as plt + +matplotlib.rcParams['axes.unicode_minus'] = False +fig = plt.figure() +ax = fig.add_subplot(111) +ax.plot(10*np.random.randn(100), 10*np.random.randn(100), 'o') +ax.set_title('Using hypen instead of unicode minus') +plt.show() Modified: trunk/matplotlib/lib/matplotlib/projections/polar.py =================================================================== --- trunk/matplotlib/lib/matplotlib/projections/polar.py 2008-11-25 21:14:13 UTC (rev 6452) +++ trunk/matplotlib/lib/matplotlib/projections/polar.py 2008-11-25 21:43:36 UTC (rev 6453) @@ -139,6 +139,11 @@ if rcParams['text.usetex'] and not rcParams['text.latex.unicode']: return r"$%d^\circ$" % ((x / npy.pi) * 180.0) else: + # we use unicode, rather than mathtext with \circ, so + # that it will work correctly with any arbitrary font + # (assuming it has a degree sign), whereas $5\circ$ + # will only work correctly with one of the supported + # math fonts (Computer Modern and STIX) return u"%d\u00b0" % ((x / npy.pi) * 180.0) class RadialLocator(Locator): Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-11-25 21:14:13 UTC (rev 6452) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-11-25 21:43:36 UTC (rev 6453) @@ -412,6 +412,7 @@ # use scientific notation if log10 # of the axis range is smaller than the # first or larger than the second + 'axes.unicode_minus' : [True, validate_bool], 'polaraxes.grid' : [True, validate_bool], # display polar grid or not Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2008-11-25 21:14:13 UTC (rev 6452) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2008-11-25 21:43:36 UTC (rev 6453) @@ -313,7 +313,7 @@ def fix_minus(self, s): 'use a unicode minus rather than hyphen' - if rcParams['text.usetex']: return s + if rcParams['text.usetex'] or not rcParams['axes.unicode_minus']: return s else: return s.replace('-', u'\u2212') def __call__(self, x, pos=None): Modified: trunk/matplotlib/matplotlibrc.template =================================================================== --- trunk/matplotlib/matplotlibrc.template 2008-11-25 21:14:13 UTC (rev 6452) +++ trunk/matplotlib/matplotlibrc.template 2008-11-25 21:43:36 UTC (rev 6453) @@ -205,6 +205,8 @@ #axes.formatter.limits : -7, 7 # use scientific notation if log10 # of the axis range is smaller than the # first or larger than the second +#axes.unicode_minus : True # use unicode for the minus symbol + # rather than hypen. See http://en.wikipedia.org/wiki/Plus_sign#Plus_sign #polaraxes.grid : True # display grid on polar axes This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-11-26 17:43:12
|
Revision: 6454 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6454&view=rev Author: jdh2358 Date: 2008-11-26 17:43:09 +0000 (Wed, 26 Nov 2008) Log Message: ----------- added figure/axes enter/leave events Modified Paths: -------------- trunk/matplotlib/doc/users/event_handling.rst trunk/matplotlib/lib/matplotlib/backend_bases.py Added Paths: ----------- trunk/matplotlib/examples/event_handling/figure_axes_enter_leave.py Modified: trunk/matplotlib/doc/users/event_handling.rst =================================================================== --- trunk/matplotlib/doc/users/event_handling.rst 2008-11-25 21:43:36 UTC (rev 6453) +++ trunk/matplotlib/doc/users/event_handling.rst 2008-11-26 17:43:09 UTC (rev 6454) @@ -53,15 +53,19 @@ ======================= ====================================================================================== Event name Class and description ======================= ====================================================================================== -'button_press_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is pressed -'button_release_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is released -'draw_event' :class:`~matplotlib.backend_bases.DrawEvent` - canvas draw -'key_press_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is pressed -'key_release_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is released -'motion_notify_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse motion -'pick_event' :class:`~matplotlib.backend_bases.PickEvent` - an object in the canvas is selected -'resize_event' :class:`~matplotlib.backend_bases.ResizeEvent` - figure canvas is resized -'scroll_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse scroll wheel is rolled +'button_press_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is pressed +'button_release_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is released +'draw_event' :class:`~matplotlib.backend_bases.DrawEvent` - canvas draw +'key_press_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is pressed +'key_release_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is released +'motion_notify_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse motion +'pick_event' :class:`~matplotlib.backend_bases.PickEvent` - an object in the canvas is selected +'resize_event' :class:`~matplotlib.backend_bases.ResizeEvent` - figure canvas is resized +'scroll_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse scroll wheel is rolled +'figure_enter_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse enters a new figure +'figure_leave_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse leaves a figure +'axes_enter_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse enters a new axes +'axes_leave_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse leaves an axes ======================= ====================================================================================== .. _event-attributes: @@ -330,6 +334,66 @@ plt.show() +.. _enter-leave-events: + +Mouse enter and leave +====================== + +If you want to be notified when the mouse enters or leaves a figure or +axes, you can connect to the figure/axes enter/leave events. Here is +a simple example that changes the colors of the axes and figure +background that the mouse is over:: + + """ + Illustrate the figure and axes enter and leave events by changing the + frame colors on enter and leave + """ + import matplotlib.pyplot as plt + + def enter_axes(event): + print 'enter_axes', event.inaxes + event.inaxes.patch.set_facecolor('yellow') + event.canvas.draw() + + def leave_axes(event): + print 'leave_axes', event.inaxes + event.inaxes.patch.set_facecolor('white') + event.canvas.draw() + + def enter_figure(event): + print 'enter_figure', event.canvas.figure + event.canvas.figure.patch.set_facecolor('red') + event.canvas.draw() + + def leave_figure(event): + print 'leave_figure', event.canvas.figure + event.canvas.figure.patch.set_facecolor('grey') + event.canvas.draw() + + fig1 = plt.figure() + fig1.suptitle('mouse hover over figure or axes to trigger events') + ax1 = fig1.add_subplot(211) + ax2 = fig1.add_subplot(212) + + fig1.canvas.mpl_connect('figure_enter_event', enter_figure) + fig1.canvas.mpl_connect('figure_leave_event', leave_figure) + fig1.canvas.mpl_connect('axes_enter_event', enter_axes) + fig1.canvas.mpl_connect('axes_leave_event', leave_axes) + + fig2 = plt.figure() + fig2.suptitle('mouse hover over figure or axes to trigger events') + ax1 = fig2.add_subplot(211) + ax2 = fig2.add_subplot(212) + + fig2.canvas.mpl_connect('figure_enter_event', enter_figure) + fig2.canvas.mpl_connect('figure_leave_event', leave_figure) + fig2.canvas.mpl_connect('axes_enter_event', enter_axes) + fig2.canvas.mpl_connect('axes_leave_event', leave_axes) + + plt.show() + + + .. _object-picking: Object picking Added: trunk/matplotlib/examples/event_handling/figure_axes_enter_leave.py =================================================================== --- trunk/matplotlib/examples/event_handling/figure_axes_enter_leave.py (rev 0) +++ trunk/matplotlib/examples/event_handling/figure_axes_enter_leave.py 2008-11-26 17:43:09 UTC (rev 6454) @@ -0,0 +1,49 @@ +""" +Illustrate the figure and axes enter and leave events by changing the +frame colors on enter and leave +""" +import matplotlib.pyplot as plt + +def enter_axes(event): + print 'enter_axes', event.inaxes + event.inaxes.patch.set_facecolor('yellow') + event.canvas.draw() + +def leave_axes(event): + print 'leave_axes', event.inaxes + event.inaxes.patch.set_facecolor('white') + event.canvas.draw() + +def enter_figure(event): + print 'enter_figure', event.canvas.figure + event.canvas.figure.patch.set_facecolor('red') + event.canvas.draw() + +def leave_figure(event): + print 'leave_figure', event.canvas.figure + event.canvas.figure.patch.set_facecolor('grey') + event.canvas.draw() + +fig1 = plt.figure() +fig1.suptitle('mouse hover over figure or axes to trigger events') +ax1 = fig1.add_subplot(211) +ax2 = fig1.add_subplot(212) + +fig1.canvas.mpl_connect('figure_enter_event', enter_figure) +fig1.canvas.mpl_connect('figure_leave_event', leave_figure) +fig1.canvas.mpl_connect('axes_enter_event', enter_axes) +fig1.canvas.mpl_connect('axes_leave_event', leave_axes) + +fig2 = plt.figure() +fig2.suptitle('mouse hover over figure or axes to trigger events') +ax1 = fig2.add_subplot(211) +ax2 = fig2.add_subplot(212) + +fig2.canvas.mpl_connect('figure_enter_event', enter_figure) +fig2.canvas.mpl_connect('figure_leave_event', leave_figure) +fig2.canvas.mpl_connect('axes_enter_event', enter_axes) +fig2.canvas.mpl_connect('axes_leave_event', leave_axes) + +plt.show() + + Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-25 21:43:36 UTC (rev 6453) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-11-26 17:43:09 UTC (rev 6454) @@ -743,6 +743,9 @@ xdata = None # x coord of mouse in data coords ydata = None # y coord of mouse in data coords + # the last event that was triggered before this one + _lastevent = None + def __init__(self, name, canvas, x, y,guiEvent=None): """ *x*, *y* in figure coords, 0,0 = bottom, left @@ -751,8 +754,12 @@ self.x = x self.y = y + + if x is None or y is None: # cannot check if event was in axes if no x,y info + self.inaxes = False + self._update_enter_leave() return # Find all axes containing the mouse @@ -760,6 +767,7 @@ if len(axes_list) == 0: # None found self.inaxes = None + self._update_enter_leave() return elif (len(axes_list) > 1): # Overlap, get the highest zorder axCmp = lambda _x,_y: cmp(_x.zorder, _y.zorder) @@ -777,6 +785,36 @@ self.xdata = xdata self.ydata = ydata + self._update_enter_leave() + + def _update_enter_leave(self): + 'process the figure/axes enter leave events' + if LocationEvent._lastevent is not None: + last = LocationEvent._lastevent + if last.canvas!=self.canvas: + # process figure enter/leave event + last.canvas.callbacks.process('figure_leave_event', last) + self.canvas.callbacks.process('figure_enter_event', self) + if last.inaxes!=self.inaxes: + # process axes enter/leave events + if last.inaxes is not None: + last.canvas.callbacks.process('axes_leave_event', last) + if self.inaxes is not None: + self.canvas.callbacks.process('axes_enter_event', self) + + else: + # process a figure enter event + self.canvas.callbacks.process('figure_enter_event', self) + # process an axes enter event if we are over an axes + if self.inaxes is not None: + self.canvas.callbacks.process('axes_enter_event', self) + + + LocationEvent._lastevent = self + + + + class MouseEvent(LocationEvent): """ A mouse event ('button_press_event', 'button_release_event', 'scroll_event', @@ -914,6 +952,12 @@ 'motion_notify_event', 'pick_event', 'idle_event', + 'figure_enter_event', + # todo: we only process this when a mouse enters a different + # figure -- we need to connect to the GUI leavel event + 'figure_leave_event', + 'axes_enter_event', + 'axes_leave_event' ] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mme...@us...> - 2008-12-01 10:10:46
|
Revision: 6459 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6459&view=rev Author: mmetz_bn Date: 2008-12-01 10:10:39 +0000 (Mon, 01 Dec 2008) Log Message: ----------- Fixed histogram autoscaling bug when bins or range are given explicitly Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-11-30 18:33:55 UTC (rev 6458) +++ trunk/matplotlib/CHANGELOG 2008-12-01 10:10:39 UTC (rev 6459) @@ -1,3 +1,6 @@ +2008-12-01 Fixed histogram autoscaling bug when bins or range are given + explicitly (fixes Debian bug 503148) - MM + 2008-11-25 Added rcParam axes.unicode_minus which allows plain hypen for minus when False - JDH Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-11-30 18:33:55 UTC (rev 6458) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-12-01 10:10:39 UTC (rev 6459) @@ -6510,6 +6510,9 @@ """ if not self._hold: self.cla() + # NOTE: the range keyword overwrites the built-in func range !!! + # needs to be fixed in with numpy !!! + if kwargs.get('width') is not None: raise DeprecationWarning( 'hist now uses the rwidth to give relative width ' @@ -6531,6 +6534,10 @@ tx.append( np.array(x[i]) ) x = tx + # Check whether bins or range are given explicitly. In that + # case do not autoscale axes. + binsgiven = (cbook.iterable(bins) or range != None) + n = [] for i in xrange(len(x)): # this will automatically overwrite bins, @@ -6541,9 +6548,8 @@ if cumulative: slc = slice(None) - if cbook.is_numlike(cumulative): - if cumulative < 0: - slc = slice(None,None,-1) + if cbook.is_numlike(cumulative) and cumulative < 0: + slc = slice(None,None,-1) if normed: n = [(m * np.diff(bins))[slc].cumsum()[slc] for m in n] @@ -6675,6 +6681,16 @@ p.set_label(label) label = '_nolegend_' + if binsgiven: + self.set_autoscale_on(False) + if orientation == 'vertical': + self.autoscale_view(scalex=False, scaley=True) + XL = self.xaxis.get_major_locator().view_limits(bins[0], bins[-1]) + self.set_xbound(XL) + else: + self.autoscale_view(scalex=True, scaley=False) + YL = self.yaxis.get_major_locator().view_limits(bins[0], bins[-1]) + self.set_ybound(YL) if len(n)==1: return n[0], bins, cbook.silent_list('Patch', patches[0]) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-01 14:51:40
|
Revision: 6461 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6461&view=rev Author: jdh2358 Date: 2008-12-01 14:51:35 +0000 (Mon, 01 Dec 2008) Log Message: ----------- added Jae Joon's legend and offsetbox implementation Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/legend.py trunk/matplotlib/lib/matplotlib/rcsetup.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/legend_demo3.py trunk/matplotlib/lib/matplotlib/offsetbox.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-01 14:06:49 UTC (rev 6460) +++ trunk/matplotlib/CHANGELOG 2008-12-01 14:51:35 UTC (rev 6461) @@ -1,3 +1,6 @@ +2008-11-30 Reimplementaion of the legend which supports baseline alignement, + multi-column, and expand mode. - JJL + 2008-12-01 Fixed histogram autoscaling bug when bins or range are given explicitly (fixes Debian bug 503148) - MM Added: trunk/matplotlib/examples/pylab_examples/legend_demo3.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/legend_demo3.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2008-12-01 14:51:35 UTC (rev 6461) @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +import matplotlib.pyplot as plt +import numpy as np + +def myplot(ax): + t1 = np.arange(0.0, 1.0, 0.1) + for n in [1, 2, 3, 4]: + ax.plot(t1, t1**n, label="n=%d"%(n,)) + +ax1 = plt.subplot(3,1,1) +ax1.plot([1], label="multi\nline") +ax1.plot([1], label="$2^{2^2}$") +ax1.plot([1], label=r"$\frac{1}{2}\pi$") +ax1.legend(loc=1, ncol=3, shadow=True) + +ax2 = plt.subplot(3,1,2) +myplot(ax2) +ax2.legend(loc=1, ncol=2, shadow=True) + + +ax3 = plt.subplot(3,1,3) +myplot(ax3) +ax3.legend(loc=1, ncol=4, mode="expand", shadow=True) + + +#title('Damped oscillation') + +plt.draw() +plt.show() + +#plt.savefig("legend_demo3") + + Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-01 14:06:49 UTC (rev 6460) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-01 14:51:35 UTC (rev 6461) @@ -26,16 +26,21 @@ import numpy as np from matplotlib import rcParams -from artist import Artist -from cbook import is_string_like, iterable, silent_list, safezip -from font_manager import FontProperties -from lines import Line2D -from mlab import segments_intersect -from patches import Patch, Rectangle, Shadow, bbox_artist -from collections import LineCollection, RegularPolyCollection -from text import Text -from transforms import Affine2D, Bbox, BboxTransformTo +from matplotlib.artist import Artist +from matplotlib.cbook import is_string_like, iterable, silent_list, safezip +from matplotlib.font_manager import FontProperties +from matplotlib.lines import Line2D +from matplotlib.mlab import segments_intersect +from matplotlib.patches import Patch, Rectangle, Shadow, bbox_artist, FancyBboxPatch +from matplotlib.collections import LineCollection, RegularPolyCollection +from matplotlib.text import Text +from matplotlib.transforms import Affine2D, Bbox, BboxTransformTo +from itertools import cycle, izip + +from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea + + class Legend(Artist): """ Place a legend on the axes at location loc. Labels are a @@ -75,7 +80,6 @@ } - zorder = 5 def __str__(self): return "Legend" @@ -83,72 +87,132 @@ def __init__(self, parent, handles, labels, loc = None, numpoints = None, # the number of points in the legend line + markerscale = None, # the relative size of legend markers vs. original scatterpoints = 3, # TODO: may be an rcParam - prop = None, - pad = None, # the fractional whitespace inside the legend border - borderpad = None, - markerscale = None, # the relative size of legend markers vs. original + scatteryoffsets=None, + prop = None, # properties for the legend texts + # the following dimensions are in axes coords - labelsep = None, # the vertical space between the legend entries - handlelen = None, # the length of the legend lines - handletextsep = None, # the space between the legend line and legend text - axespad = None, # the border between the axes and legend edge + pad = None, # deprecated; use borderpad + labelsep = None, # deprecated; use labelspacing + handlelen = None, # deprecated; use handlelength + handletextsep = None, # deprecated; use handletextpad + axespad = None, # deprecated; use borderaxespad + + # spacing & pad defined as a fractionof the font-size + borderpad = None, # the fractional whitespace inside the legend border + labelspacing=None, #the vertical space between the legend entries + handlelength=None, # the length of the legend handles + handletextpad=None, # the pad between the legend handle and text + borderaxespad=None, # the pad between the axes and legend border + columnspacing=None, # spacing between columns + + ncol=1, # number of columns + mode=None, # mode for horizontal distribution of columns. None, "expand" + shadow = None, - scatteryoffsets=None, ): """ - parent # the artist that contains the legend - handles # a list of artists (lines, patches) to add to the legend - labels # a list of strings to label the legend - loc # a location code - numpoints = 4 # the number of points in the legend line - scatterpoints = 3 # the number of points for the scatterplot legend - prop = FontProperties(size='smaller') # the font property - pad = 0.2 # the fractional whitespace inside the legend border - markerscale = 0.6 # the relative size of legend markers vs. original - shadow # if True, draw a shadow behind legend - scatteryoffsets # a list of yoffsets for scatter symbols in legend + - *parent* : the artist that contains the legend + - *handles* : a list of artists (lines, patches) to add to the legend + - *labels* : a list of strings to label the legend -The following dimensions are in axes coords - labelsep = 0.005 # the vertical space between the legend entries - handlelen = 0.05 # the length of the legend lines - handletextsep = 0.02 # the space between the legend line and legend text - axespad = 0.02 # the border between the axes and legend edge + Optional keyword arguments: + + ================ ========================================= + Keyword Description + ================ ========================================= + + loc a location code + numpoints the number of points in the legend line + prop the font property + markerscale the relative size of legend markers vs. original + shadow if True, draw a shadow behind legend + scatteryoffsets a list of yoffsets for scatter symbols in legend + + borderpad the fractional whitespace inside the legend border + labelspacing the vertical space between the legend entries + handlelength the length of the legend handles + handletextpad the pad between the legend handle and text + borderaxespad the pad between the axes and legend border + columnspacing the spacing between columns + +The dimensions of pad and spacing are given as a fraction of the +fontsize. Values from rcParams will be used if None. + """ - from axes import Axes # local import only to avoid circularity - from figure import Figure # local import only to avoid circularity + from matplotlib.axes import Axes # local import only to avoid circularity + from matplotlib.figure import Figure # local import only to avoid circularity Artist.__init__(self) - proplist=[numpoints, scatterpoints, pad, borderpad, markerscale, labelsep, - handlelen, handletextsep, axespad, shadow] - propnames=['numpoints','scatterpoints', 'pad', 'borderpad', 'markerscale', - 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow'] - for name, value in safezip(propnames,proplist): - if value is None: - value=rcParams["legend."+name] - setattr(self,name,value) - if pad: - warnings.warn("Use 'borderpad' instead of 'pad'.", DeprecationWarning) - # 2008/10/04 - if self.numpoints <= 0: - raise ValueError("numpoints must be > 0; it was %d"% numpoints) - if self.scatterpoints <= 0: - raise ValueError("scatterpoints must be > 0; it was %d"% numpoints) if prop is None: self.prop=FontProperties(size=rcParams["legend.fontsize"]) else: self.prop=prop self.fontsize = self.prop.get_size_in_points() + propnames=['numpoints', 'markerscale', 'shadow', "columnspacing", + "scatterpoints"] + + localdict = locals() + + for name in propnames: + if localdict[name] is None: + value = rcParams["legend."+name] + else: + value = localdict[name] + setattr(self, name, value) + + # Take care the deprecated keywords + deprecated_kwds = {"pad":"borderpad", + "labelsep":"labelspacing", + "handlelen":"handlelength", + "handletextsep":"handletextpad", + "axespad":"borderaxespad"} + + # convert values of deprecated keywords (ginve in axes coords) + # to new vaules in a fraction of the font size + + # conversion factor + bbox = parent.bbox + axessize_fontsize = min(bbox.width, bbox.height)/self.fontsize + + for k, v in deprecated_kwds.items(): + # use deprecated value if not None and if their newer + # counter part is None. + if localdict[k] is not None and localdict[v] is None: + warnings.warn("Use '%s' instead of '%s'." % (v, k), + DeprecationWarning) + setattr(self, v, localdict[k]*axessize_fontsize) + continue + + # Otherwise, use new keywords + if localdict[v] is None: + setattr(self, v, rcParams["legend."+v]) + else: + setattr(self, v, localdict[v]) + + del localdict + + self._ncol = ncol + + if self.numpoints <= 0: + raise ValueError("numpoints must be >= 0; it was %d"% numpoints) + # introduce y-offset for handles of the scatter plot if scatteryoffsets is None: - self._scatteryoffsets = np.array([4./8., 5./8., 3./8.]) + self._scatteryoffsets = np.array([3./8., 4./8., 2.5/8.]) else: self._scatteryoffsets = np.asarray(scatteryoffsets) - reps = int(self.scatterpoints / len(self._scatteryoffsets)) + 1 + reps = int(self.numpoints / len(self._scatteryoffsets)) + 1 self._scatteryoffsets = np.tile(self._scatteryoffsets, reps)[:self.scatterpoints] + # _legend_box is an OffsetBox instance that contains all + # legend items and will be initialized from _init_legend_box() + # method. + self._legend_box = None + if isinstance(parent,Axes): self.isaxes = True self.set_figure(parent.figure) @@ -158,9 +222,6 @@ else: raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent - self._offsetTransform = Affine2D() - self._parentTransform = BboxTransformTo(parent.bbox) - Artist.set_transform(self, self._offsetTransform + self._parentTransform) if loc is None: loc = rcParams["legend.loc"] @@ -186,100 +247,165 @@ loc = 1 self._loc = loc + self._mode = mode - self.legendPatch = Rectangle( - xy=(0.0, 0.0), width=0.5, height=0.5, + # We use FancyBboxPatch to draw a legend frame. The location + # and size of the box will be updated during the drawing time. + self.legendPatch = FancyBboxPatch( + xy=(0.0, 0.0), width=1., height=1., facecolor='w', edgecolor='k', + mutation_scale=self.fontsize, ) + + # The width and height of the legendPatch will be set (in the + # draw()) to the length that includes the padding. Thus we set + # pad=0 here. + self.legendPatch.set_boxstyle("round",pad=0, #self.borderpad, + rounding_size=0.2) + self._set_artist_props(self.legendPatch) - # make a trial box in the middle of the axes. relocate it - # based on it's bbox - left, top = 0.5, 0.5 - textleft = left+ self.handlelen+self.handletextsep - self.texts = self._get_texts(labels, textleft, top) - self.legendHandles = self._get_handles(handles, self.texts) - self._drawFrame = True + # populate the legend_box with legend items. + self._init_legend_box(handles, labels) + self._legend_box.set_figure(self.figure) + + def _set_artist_props(self, a): + """ + set the boilerplate props for artists added to axes + """ a.set_figure(self.figure) + + for c in self.get_children(): + c.set_figure(self.figure) + a.set_transform(self.get_transform()) - def _approx_text_height(self): - return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height + def _findoffset_best(self, width, height, xdescent, ydescent): + "Heper function to locate the legend" + ox, oy = self._find_best_position(width, height) + return ox+xdescent, oy+ydescent + def _findoffset_loc(self, width, height, xdescent, ydescent): + "Heper function to locate the legend" + bbox = Bbox.from_bounds(0, 0, width, height) + x, y = self._get_anchored_bbox(self._loc, bbox, self.parent.bbox) + return x+xdescent, y+ydescent def draw(self, renderer): + "Draw everything that belongs to the legend" if not self.get_visible(): return + renderer.open_group('legend') - self._update_positions(renderer) + + # find_offset function will be provided to _legend_box and + # _legend_box will draw itself at the location of the return + # value of the find_offset. + if self._loc == 0: + self._legend_box.set_offset(self._findoffset_best) + else: + self._legend_box.set_offset(self._findoffset_loc) + + # if mode == fill, set the width of the legend_box to the + # width of the paret (minus pads) + if self._mode in ["expand"]: + pad = 2*(self.borderaxespad+self.borderpad)*self.fontsize + self._legend_box.set_width(self.parent.bbox.width-pad) + if self._drawFrame: + # update the location and size of the legend + bbox = self._legend_box.get_window_extent(renderer) + self.legendPatch.set_bounds(bbox.x0, bbox.y0, + bbox.width, bbox.height) + if self.shadow: - shadow = Shadow(self.legendPatch, -0.005, -0.005) + shadow = Shadow(self.legendPatch, 2, -2) shadow.draw(renderer) + self.legendPatch.draw(renderer) + self._legend_box.draw(renderer) - if not len(self.legendHandles) and not len(self.texts): return - for h in self.legendHandles: - if h is not None: - h.draw(renderer) - if hasattr(h, '_legmarker'): - h._legmarker.draw(renderer) - if 0: bbox_artist(h, renderer) - - for t in self.texts: - if 0: bbox_artist(t, renderer) - t.draw(renderer) renderer.close_group('legend') - #draw_bbox(self.save, renderer, 'g') - #draw_bbox(self.ibox, renderer, 'r', self.get_transform()) - def _get_handle_text_bbox(self, renderer): - 'Get a bbox for the text and lines in axes coords' - bboxesText = [t.get_window_extent(renderer) for t in self.texts] - bboxesHandles = [h.get_window_extent(renderer) for h in self.legendHandles if h is not None] + def _approx_text_height(self): + """ + Return the approximate height of the text. This is used to place + the legend handle. + """ + return self.fontsize/72.0*self.figure.dpi - bboxesAll = bboxesText - bboxesAll.extend(bboxesHandles) - bbox = Bbox.union(bboxesAll) - self.save = bbox + def _init_legend_box(self, handles, labels): + """ + Initiallize the legend_box. The legend_box is an instance of + the OffsetBox, which is packed with legend handles and + texts. Once packed, their location is calculated during the + drawing time. + """ - ibox = bbox.inverse_transformed(self.get_transform()) - self.ibox = ibox + # legend_box is a HPacker, horizontally packed with + # columns. Each column is a VPacker, vertically packed with + # legend items. Each legend item is HPacker packed with + # legend handleBox and labelBox. handleBox is an instance of + # offsetbox.DrawingArea which contains legend handle. labelBox + # is an instance of offsetbox.TextArea which contains legend + # text. - return ibox + + text_list = [] # the list of text instances + handle_list = [] # the list of text instances - def _get_handles(self, handles, texts): - handles = list(handles) - texts = list(texts) - HEIGHT = self._approx_text_height() - left = 0.5 + label_prop = dict(verticalalignment='baseline', + horizontalalignment='left', + fontproperties=self.prop, + ) - ret = [] # the returned legend lines + labelboxes = [] - # we need to pad the text with empties for the numpoints=1 - # centered marker proxy + for l in labels: + textbox = TextArea(l, textprops=label_prop) + text_list.append(textbox._text) + labelboxes.append(textbox) - for handle, label in safezip(handles, texts): + handleboxes = [] + + + # The approximate height and descent of text. These values are + # only used for plotting the legend handle. + height = self._approx_text_height() * 0.6 + descent = 0. #height/6. + + # each handle needs to be drawn inside a box of + # (x, y, w, h) = (0, -descent, width, height). + # And their corrdinates should be given in the display coordinates. + + # The transformation of each handle will be automatically set + # to self.get_trasnform(). If the artist does not uses its + # default trasnform (eg, Collections), you need to + # manually set their transform to the self.get_transform(). + + for handle in handles: if isinstance(handle, RegularPolyCollection): npoints = self.scatterpoints else: npoints = self.numpoints if npoints > 1: - xdata = np.linspace(left, left + self.handlelen, npoints) + # we put some pad here to compensate the size of the + # marker + xdata = np.linspace(0.3*self.fontsize, + (self.handlelength-0.3)*self.fontsize, + npoints) xdata_marker = xdata elif npoints == 1: - xdata = np.linspace(left, left + self.handlelen, 2) - xdata_marker = [left + 0.5*self.handlelen] + xdata = np.linspace(0, self.handlelength, 2) + xdata_marker = [0.5*self.handlelength*self.fontsize] - x, y = label.get_position() - x -= self.handlelen + self.handletextsep if isinstance(handle, Line2D): - ydata = (y-HEIGHT/2)*np.ones(xdata.shape, float) + ydata = ((height-descent)/2.)*np.ones(xdata.shape, float) legline = Line2D(xdata, ydata) legline.update_from(handle) @@ -288,8 +414,9 @@ legline.set_clip_path(None) legline.set_drawstyle('default') legline.set_marker('None') - ret.append(legline) + handle_list.append(legline) + legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)]) legline_marker.update_from(handle) self._set_artist_props(legline_marker) @@ -302,16 +429,17 @@ legline._legmarker = legline_marker elif isinstance(handle, Patch): - p = Rectangle(xy=(min(xdata), y-3/4*HEIGHT), - width = self.handlelen, height=HEIGHT/2, + p = Rectangle(xy=(0, -0.*descent), + width = self.handlelength*self.fontsize, + height=0.*descent+(height-descent)*.9, ) p.update_from(handle) self._set_artist_props(p) p.set_clip_box(None) p.set_clip_path(None) - ret.append(p) + handle_list.append(p) elif isinstance(handle, LineCollection): - ydata = (y-HEIGHT/2)*np.ones(xdata.shape, float) + ydata = ((height-descent)/2.)*np.ones(xdata.shape, float) legline = Line2D(xdata, ydata) self._set_artist_props(legline) legline.set_clip_box(None) @@ -322,13 +450,13 @@ legline.set_color(color) legline.set_linewidth(lw) legline.set_dashes(dashes) - ret.append(legline) + handle_list.append(legline) elif isinstance(handle, RegularPolyCollection): - # the ydata values set here have no effects as it will - # be updated in the _update_positions() method. - ydata = (y-HEIGHT/2)*np.ones(np.asarray(xdata_marker).shape, float) + #ydata = self._scatteryoffsets + ydata = height*self._scatteryoffsets + size_max, size_min = max(handle.get_sizes()),\ min(handle.get_sizes()) # we may need to scale these sizes by "markerscale" @@ -338,32 +466,86 @@ sizes = [.5*(size_max+size_min), size_max, size_min] else: - sizes = size_max*np.linspace(0,1,self.scatterpoints)+size_min - + sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min + p = type(handle)(handle.get_numsides(), rotation=handle.get_rotation(), sizes=sizes, offsets=zip(xdata_marker,ydata), - transOffset=self.get_transform()) - + transOffset=self.get_transform(), + ) + p.update_from(handle) p.set_figure(self.figure) p.set_clip_box(None) p.set_clip_path(None) - ret.append(p) + handle_list.append(p) else: - ret.append(None) + handle_list.append(None) - return ret + handlebox = DrawingArea(width=self.handlelength*self.fontsize, + height=height, + xdescent=0., ydescent=descent) + handle = handle_list[-1] + handlebox.add_artist(handle) + if hasattr(handle, "_legmarker"): + handlebox.add_artist(handle._legmarker) + handleboxes.append(handlebox) + + + # We calculate number of lows in each column. The first + # (num_largecol) columns will have (nrows+1) rows, and remaing + # (num_smallcol) columns will have (nrows) rows. + nrows, num_largecol = divmod(len(handleboxes), self._ncol) + num_smallcol = self._ncol-num_largecol + + # starting index of each column and number of rows in it. + largecol = zip(range(0, num_largecol*(nrows+1), (nrows+1)), + [nrows+1] * num_largecol) + smallcol = zip(range(num_largecol*(nrows+1), len(handleboxes), nrows), + [nrows] * num_smallcol) + + handle_label = zip(handleboxes, labelboxes) + columnbox = [] + for i0, di in largecol+smallcol: + # pack handleBox and labelBox into itemBox + itemBoxes = [HPacker(pad=0, + sep=self.handletextpad*self.fontsize, + children=[h, t], align="baseline") + for h, t in handle_label[i0:i0+di]] + + # pack columnBox + columnbox.append(VPacker(pad=0, + sep=self.labelspacing*self.fontsize, + align="baseline", + children=itemBoxes)) + + if self._mode == "expand": + mode = "expand" + else: + mode = "fixed" + + sep = self.columnspacing*self.fontsize + + self._legend_box = HPacker(pad=self.borderpad*self.fontsize, + sep=sep, align="baseline", + mode=mode, + children=columnbox) + + self.texts = text_list + self.legendHandles = handle_list + + def _auto_legend_data(self): - """ Returns list of vertices and extents covered by the plot. + """ + Returns list of vertices and extents covered by the plot. Returns a two long list. First element is a list of (x, y) vertices (in - axes-coordinates) covered by all the lines and line + display-coordinates) covered by all the lines and line collections, in the legend's handles. Second element is a list of bounding boxes for all the patches in @@ -377,24 +559,21 @@ bboxes = [] lines = [] - inverse_transform = ax.transAxes.inverted() - for handle in ax.lines: assert isinstance(handle, Line2D) path = handle.get_path() trans = handle.get_transform() tpath = trans.transform_path(path) - apath = inverse_transform.transform_path(tpath) - lines.append(apath) + lines.append(tpath) for handle in ax.patches: assert isinstance(handle, Patch) if isinstance(handle, Rectangle): - transform = handle.get_data_transform() + inverse_transform + transform = handle.get_data_transform() bboxes.append(handle.get_bbox().transformed(transform)) else: - transform = handle.get_transform() + inverse_transform + transform = handle.get_transform() bboxes.append(handle.get_path().get_extents(transform)) return [vertices, bboxes, lines] @@ -404,9 +583,10 @@ self._drawFrame = b def get_children(self): + 'return a list of child artists' children = [] - children.extend(self.legendHandles) - children.extend(self.texts) + if self._legend_box: + children.append(self._legend_box) return children def get_frame(self): @@ -425,51 +605,61 @@ 'return a list of text.Text instance in the legend' return silent_list('Text', self.texts) - def _get_texts(self, labels, left, upper): + def get_window_extent(self): + 'return a extent of the the legend' + return self.legendPatch.get_window_extent() - # height in axes coords - HEIGHT = self._approx_text_height() - pos = upper - x = left - ret = [] # the returned list of text instances - for l in labels: - text = Text( - x=x, y=pos, - text=l, - fontproperties=self.prop, - verticalalignment='top', - horizontalalignment='left' - ) - self._set_artist_props(text) - ret.append(text) - pos -= HEIGHT + def _get_anchored_bbox(self, loc, bbox, parentbbox): + """ + Place the *bbox* inside the *parentbbox* according to a given + location code. Return the (x,y) coordinate of the bbox. - return ret + - loc: a location code in range(1, 11). + This corresponds to the possible values for self._loc, excluding "best". + - bbox: bbox to be placed, display coodinate units. + - parentbbox: a parent box which will contain the bbox. In + display coordinates. + """ + assert loc in range(1,11) # called only internally - def get_window_extent(self): - return self.legendPatch.get_window_extent() + BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11) + anchor_coefs={UR:"NE", + UL:"NW", + LL:"SW", + LR:"SE", + R:"E", + CL:"W", + CR:"E", + LC:"S", + UC:"N", + C:"C"} + + c = anchor_coefs[loc] - def _offset(self, ox, oy): - 'Move all the artists by ox,oy (axes coords)' - self._offsetTransform.clear().translate(ox, oy) + container = parentbbox.padded(-(self.borderaxespad) * self.fontsize) + anchored_box = bbox.anchored(c, container=container) + return anchored_box.x0, anchored_box.y0 + def _find_best_position(self, width, height, consider=None): - """Determine the best location to place the legend. + """ + Determine the best location to place the legend. `consider` is a list of (x, y) pairs to consider as a potential - lower-left corner of the legend. All are axes coords. + lower-left corner of the legend. All are display coords. """ assert self.isaxes # should always hold because function is only called internally verts, bboxes, lines = self._auto_legend_data() - consider = [self._loc_to_axes_coords(x, width, height) for x in range(1, len(self.codes))] + bbox = Bbox.from_bounds(0, 0, width, height) + consider = [self._get_anchored_bbox(x, bbox, self.parent.bbox) for x in range(1, len(self.codes))] - tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y() + #tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y() candidates = [] for l, b in consider: @@ -481,15 +671,16 @@ if line.intersects_bbox(legendBox): badness += 1 - ox, oy = l-tx, b-ty + ox, oy = l, b if badness == 0: return ox, oy - candidates.append((badness, (ox, oy))) + candidates.append((badness, (l, b))) # rather than use min() or list.sort(), do this so that we are assured # that in the case of two equal badnesses, the one first considered is # returned. + # NOTE: list.sort() is stable.But leave as it is for now. -JJL minCandidate = candidates[0] for candidate in candidates: if candidate[0] < minCandidate[0]: @@ -499,103 +690,3 @@ return ox, oy - - def _loc_to_axes_coords(self, loc, width, height): - """Convert a location code to axes coordinates. - - - loc: a location code in range(1, 11). - This corresponds to the possible values for self._loc, excluding "best". - - - width, height: the final size of the legend, axes units. - """ - assert loc in range(1,11) # called only internally - - BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11) - - if loc in (UL, LL, CL): # left - x = self.axespad - elif loc in (UR, LR, CR, R): # right - x = 1.0 - (width + self.axespad) - elif loc in (LC, UC, C): # center x - x = (0.5 - width/2.0) - - if loc in (UR, UL, UC): # upper - y = 1.0 - (height + self.axespad) - elif loc in (LL, LR, LC): # lower - y = self.axespad - elif loc in (CL, CR, C, R): # center y - y = (0.5 - height/2.0) - - return x,y - - - def _update_positions(self, renderer): - # called from renderer to allow more precise estimates of - # widths and heights with get_window_extent - - if not len(self.legendHandles) and not len(self.texts): return - def get_tbounds(text): #get text bounds in axes coords - bbox = text.get_window_extent(renderer) - bboxa = bbox.inverse_transformed(self.get_transform()) - return bboxa.bounds - - hpos = [] - for t, tabove in safezip(self.texts[1:], self.texts[:-1]): - x,y = t.get_position() - l,b,w,h = get_tbounds(tabove) - b -= self.labelsep - h += 2*self.labelsep - hpos.append( (b,h) ) - t.set_position( (x, b-0.1*h) ) - - # now do the same for last line - - l,b,w,h = get_tbounds(self.texts[-1]) - b -= self.labelsep - h += 2*self.labelsep - hpos.append( (b,h) ) - - for handle, tup in safezip(self.legendHandles, hpos): - y,h = tup - if isinstance(handle, Line2D): - ydata = y*np.ones(handle.get_xdata().shape, float) - handle.set_ydata(ydata+h/2.) - if hasattr(handle, '_legmarker'): - handle._legmarker.set_ydata(ydata+h/2.) - elif isinstance(handle, Rectangle): - handle.set_y(y+1/4*h) - handle.set_height(h/2) - elif isinstance(handle,RegularPolyCollection): - offsets = handle.get_offsets() - offsets[:,1] = y+h*self._scatteryoffsets - handle.set_offsets(offsets) - - # Set the data for the legend patch - bbox = self._get_handle_text_bbox(renderer) - - if self.pad: - bbox = bbox.expanded(1 + self.pad, 1 + self.pad) - else: - bbox = bbox.transformed(self.get_transform()) - bbox = bbox.padded(self.borderpad*self.fontsize) - bbox = bbox.inverse_transformed(self.get_transform()) - l, b, w, h = bbox.bounds - - self.legendPatch.set_bounds(l, b, w, h) - - ox, oy = 0, 0 # center - - if iterable(self._loc) and len(self._loc)==2: - xo = self.legendPatch.get_x() - yo = self.legendPatch.get_y() - x, y = self._loc - ox, oy = x-xo, y-yo - elif self._loc == 0: # "best" - ox, oy = self._find_best_position(w, h) - else: - x, y = self._loc_to_axes_coords(self._loc, w, h) - ox, oy = x-l, y-b - - self._offset(ox, oy) - -#artist.kwdocd['Legend'] = kwdoc(Legend) Added: trunk/matplotlib/lib/matplotlib/offsetbox.py =================================================================== --- trunk/matplotlib/lib/matplotlib/offsetbox.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/offsetbox.py 2008-12-01 14:51:35 UTC (rev 6461) @@ -0,0 +1,532 @@ +""" +The OffsetBox is a simple container artist. The child artist are meant +to be drawn at a relative position to its parent. The [VH]Packer, +DrawingArea and TextArea are derived from the OffsetBox. + +The [VH]Packer automatically adjust the relative postisions of their +children, which should be instances of the OffsetBox. This is used to +align similar artists together, e.g., in legend. + +The DrawingArea can contain any Artist as a child. The +DrawingArea has a fixed width and height. The position of children +relative to the parent is fixed. The TextArea is contains a single +Text instance. The width and height of the TextArea instance is the +width and height of the its child text. +""" + + +import matplotlib.transforms as mtransforms +import matplotlib.artist as martist +import matplotlib.text as mtext +import numpy as np + +from matplotlib.patches import bbox_artist as mbbox_artist +DEBUG=False +# for debuging use +def bbox_artist(*kl, **kw): + if DEBUG: + mbbox_artist(*kl, **kw) + + +# _get_packed_offsets() and _get_aligned_offsets() are coded assuming +# that we are packing boxes horizontally. But same function will be +# used with vertical packing. + +def _get_packed_offsets(wd_list, total, sep, mode="fixed"): + """ + Geiven a list of (width, xdescent) of each boxes, calculate the + total width and the x-offset positions of each items according to + *mode*. xdescent is analagous to the usual descent, but along the + x-direction. xdescent values are currently ignored. + + *wd_list* : list of (width, xdescent) of boxes to be packed. + *sep* : spacing between boxes + *total* : Intended total length. None if not used. + *mode* : packing mode. 'fixed', 'expand', or 'equal'. + """ + + w_list, d_list = zip(*wd_list) + # d_list is currently not used. + + if mode == "fixed": + offsets_ = np.add.accumulate([0]+[w + sep for w in w_list]) + offsets = offsets_[:-1] + + if total is None: + total = offsets_[-1] - sep + + return total, offsets + + elif mode == "expand": + sep = (total - sum(w_list))/(len(w_list)-1.) + offsets_ = np.add.accumulate([0]+[w + sep for w in w_list]) + offsets = offsets_[:-1] + + return total, offsets + + elif mode == "equal": + maxh = max(w_list) + if total is None: + total = (maxh+sep)*len(w_list) + else: + sep = float(total)/(len(w_list)) - maxh + + offsets = np.array([(maxh+sep)*i for i in range(len(w_list))]) + + return total, offsets + + else: + raise ValueError("Unknown mode : %s" % (mode,)) + + +def _get_aligned_offsets(hd_list, height, align="baseline"): + """ + Geiven a list of (height, descent) of each boxes, align the boxes + with *align* and calculate the y-offsets of each boxes. + total width and the offset positions of each items according to + *mode*. xdescent is analagous to the usual descent, but along the + x-direction. xdescent values are currently ignored. + + *hd_list* : list of (width, xdescent) of boxes to be aligned. + *sep* : spacing between boxes + *height* : Intended total length. None if not used. + *align* : align mode. 'baseline', 'top', 'bottom', or 'center'. + """ + + if height is None: + height = max([h for h, d in hd_list]) + + if align == "baseline": + height_descent = max([h-d for h, d in hd_list]) + descent = max([d for h, d in hd_list]) + height = height_descent + descent + offsets = [0. for h, d in hd_list] + elif align in ["left","top"]: + descent=0. + offsets = [d for h, d in hd_list] + elif align in ["right","bottom"]: + descent=0. + offsets = [height-h+d for h, d in hd_list] + elif align == "center": + descent=0. + offsets = [(height-h)*.5+d for h, d in hd_list] + else: + raise ValueError("Unknown Align mode : %s" % (align,)) + + return height, descent, offsets + + + +class OffsetBox(martist.Artist): + """ + The OffsetBox is a simple container artist. The child artist are meant + to be drawn at a relative position to its parent. + """ + def __init__(self, *kl, **kw): + + super(OffsetBox, self).__init__(*kl, **kw) + + self._children = [] + self._offset = (0, 0) + + def set_figure(self, fig): + """ + Set the figure + + accepts a class:`~matplotlib.figure.Figure` instance + """ + martist.Artist.set_figure(self, fig) + for c in self.get_children(): + c.set_figure(fig) + + def set_offset(self, xy): + """ + Set the offset + + accepts x, y, tuple, or a callable object. + """ + self._offset = xy + + def get_offset(self, width, height, xdescent, ydescent): + """ + Get the offset + + accepts extent of the box + """ + if callable(self._offset): + return self._offset(width, height, xdescent, ydescent) + else: + return self._offset + + def set_width(self, width): + """ + Set the width + + accepts float + """ + self._width = width + + def set_height(self, height): + """ + Set the height + + accepts float + """ + self._height = height + + def get_children(self): + """ + Return a list of artists it contains. + """ + return self._children + + def get_extent_offsets(self, renderer): + raise Exception("") + + def get_extent(self, renderer): + """ + Return with, height, xdescent, ydescent of box + """ + w, h, xd, yd, offsets = self.get_extent_offsets(renderer) + return w, h, xd, yd + + def get_window_extent(self, renderer): + ''' + get the bounding box in display space. + ''' + w, h, xd, yd, offsets = self.get_extent_offsets(renderer) + px, py = self.get_offset(w, h, xd, yd) + return mtransforms.Bbox.from_bounds(px-xd, py-yd, w, h) + + def draw(self, renderer): + """ + Update the location of children if necessary and draw them + to the given *renderer*. + """ + + width, height, xdescent, ydescent, offsets = self.get_extent_offsets(renderer) + + px, py = self.get_offset(width, height, xdescent, ydescent) + + for c, (ox, oy) in zip(self.get_children(), offsets): + c.set_offset((px+ox, py+oy)) + c.draw(renderer) + + bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) + + +class VPacker(OffsetBox): + """ + The VPacker has its children packed vertically. It automatically + adjust the relative postisions of children in the drawing time. + """ + def __init__(self, pad=None, sep=None, width=None, height=None, + align="baseline", mode="fixed", + children=None): + """ + *pad* : boundary pad + *sep* : spacing between items + *width*, *height* : width and height of the container box. + calculated if None. + *align* : alignment of boxes + *mode* : packing mode + """ + super(VPacker, self).__init__() + + self._height = height + self._width = width + self._align = align + self._sep = sep + self._pad = pad + self._mode = mode + + self._children = children + + + def get_extent_offsets(self, renderer): + """ + update offset of childrens and return the extents of the box + """ + + whd_list = [c.get_extent(renderer) for c in self.get_children()] + whd_list = [(w, h, xd, (h-yd)) for w, h, xd, yd in whd_list] + + + wd_list = [(w, xd) for w, h, xd, yd in whd_list] + width, xdescent, xoffsets = _get_aligned_offsets(wd_list, + self._width, + self._align) + + pack_list = [(h, yd) for w,h,xd,yd in whd_list] + height, yoffsets_ = _get_packed_offsets(pack_list, self._height, + self._sep, self._mode) + + yoffsets = yoffsets_ + [yd for w,h,xd,yd in whd_list] + ydescent = height - yoffsets[0] + yoffsets = height - yoffsets + + #w, h, xd, h_yd = whd_list[-1] + yoffsets = yoffsets - ydescent + + return width + 2*self._pad, height + 2*self._pad, \ + xdescent+self._pad, ydescent+self._pad, \ + zip(xoffsets, yoffsets) + + + +class HPacker(OffsetBox): + """ + The HPacker has its children packed horizontally. It automatically + adjust the relative postisions of children in the drawing time. + """ + def __init__(self, pad=None, width=None, height=None, sep=None, + align="baseline", mode="fixed", + children=None): + """ + *pad* : boundary pad + *sep* : spacing between items + *width*, *height* : width and height of the container box. + calculated if None. + *align* : alignment of boxes + *mode* : packing mode + """ + super(HPacker, self).__init__() + + self._height = height + self._width = width + self._align = align + + self._sep = sep + self._pad = pad + self._mode = mode + + self._children = children + + + def get_extent_offsets(self, renderer): + """ + update offset of childrens and return the extents of the box + """ + + whd_list = [c.get_extent(renderer) for c in self.get_children()] + + if self._height is None: + height_descent = max([h-yd for w,h,xd,yd in whd_list]) + ydescent = max([yd for w,h,xd,yd in whd_list]) + height = height_descent + ydescent + else: + height = self._height - 2*self._pad # width w/o pad + + hd_list = [(h, yd) for w, h, xd, yd in whd_list] + height, ydescent, yoffsets = _get_aligned_offsets(hd_list, + self._height, + self._align) + + + pack_list = [(w, xd) for w,h,xd,yd in whd_list] + width, xoffsets_ = _get_packed_offsets(pack_list, self._width, + self._sep, self._mode) + + xoffsets = xoffsets_ + [xd for w,h,xd,yd in whd_list] + + xdescent=whd_list[0][2] + xoffsets = xoffsets - xdescent + + return width + 2*self._pad, height + 2*self._pad, \ + xdescent + self._pad, ydescent + self._pad, \ + zip(xoffsets, yoffsets) + + + +class DrawingArea(OffsetBox): + """ + The DrawingArea can contain any Artist as a child. The DrawingArea + has a fixed width and height. The position of children relative to + the parent is fixed. + """ + + def __init__(self, width, height, xdescent=0., + ydescent=0., clip=True): + """ + *width*, *height* : width and height of the container box. + *xdescent*, *ydescent* : descent of the box in x- and y-direction. + """ + + super(DrawingArea, self).__init__() + + self.width = width + self.height = height + self.xdescent = xdescent + self.ydescent = ydescent + + self.offset_transform = mtransforms.Affine2D() + self.offset_transform.clear() + self.offset_transform.translate(0, 0) + + + def get_transform(self): + """ + Return the :class:`~matplotlib.transforms.Transform` applied + to the children + """ + return self.offset_transform + + def set_transform(self, t): + """ + set_transform is ignored. + """ + pass + + + def set_offset(self, xy): + """ + set offset of the container. + + Accept : tuple of x,y cooridnate in disokay units. + """ + self._offset = xy + + self.offset_transform.clear() + self.offset_transform.translate(xy[0], xy[1]) + + + def get_offset(self): + """ + return offset of the container. + """ + return self._offset + + + def get_window_extent(self, renderer): + ''' + get the bounding box in display space. + ''' + w, h, xd, yd = self.get_extent(renderer) + ox, oy = self.get_offset() #w, h, xd, yd) + return mtransforms.Bbox.from_bounds(ox-xd, oy-yd, w, h) + + + def get_extent(self, renderer): + """ + Return with, height, xdescent, ydescent of box + """ + return self.width, self.height, self.xdescent, self.ydescent + + + + def add_artist(self, a): + 'Add any :class:`~matplotlib.artist.Artist` to the container box' + self._children.append(a) + a.set_transform(self.get_transform()) + + + def draw(self, renderer): + """ + Draw the children + """ + + for c in self._children: + c.draw(renderer) + + bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) + + +class TextArea(OffsetBox): + """ + The TextArea is contains a single Text instance. The text is + placed at (0,0) with baseline+left alignment. The width and height + of the TextArea instance is the width and height of the its child + text. + """ + + + + def __init__(self, s, textprops=None, **kw): + """ + *s* : a string to be displayer. + *trnaspose* : transformation matrrix + """ + if textprops is None: + textprops = {} + + if not textprops.has_key("va"): + textprops["va"]="baseline" + + self._text = mtext.Text(0, 0, s, **textprops) + + OffsetBox.__init__(self) + + self._children = [self._text] + + + self.offset_transform = mtransforms.Affine2D() + self.offset_transform.clear() + self.offset_transform.translate(0, 0) + self._text.set_transform(self.offset_transform) + + + def set_transform(self, t): + """ + set_transform is ignored. + """ + pass + + + def set_offset(self, xy): + """ + set offset of the container. + + Accept : tuple of x,y cooridnate in disokay units. + """ + self._offset = xy + + self.offset_transform.clear() + self.offset_transform.translate(xy[0], xy[1]) + + + def get_offset(self): + """ + return offset of the container. + """ + return self._offset + + + def get_window_extent(self, renderer): + ''' + get the bounding box in display space. + ''' + w, h, xd, yd = self.get_extent(renderer) + ox, oy = self.get_offset() #w, h, xd, yd) + return mtransforms.Bbox.from_bounds(ox-xd, oy-yd, w, h) + + + def get_extent(self, renderer): + ismath = self._text.is_math_text(self._text._text) + _, h_, d_ = renderer.get_text_width_height_descent( + "lp", self._text._fontproperties, ismath=False) + + bbox, info = self._text._get_layout(renderer) + w, h = bbox.width, bbox.height + line = info[0][0] # first line + + _, hh, dd = renderer.get_text_width_height_descent( + line, self._text._fontproperties, ismath=ismath) + d = h-(hh-dd) # the baseline of the first line + + # for multiple lines, h or d may greater than h_ or d_. + h_d = max(h_ - d_, h-d) + d = max(d, d_) + h = h_d + d + + return w, h, 0., d + + + def draw(self, renderer): + """ + Draw the children + """ + + self._text.draw(renderer) + + bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) + Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-01 14:06:49 UTC (rev 6460) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-01 14:51:35 UTC (rev 6461) @@ -422,7 +422,7 @@ 'legend.numpoints' : [2, validate_int], # the number of points in the legend line 'legend.fontsize' : ['large', validate_fontsize], 'legend.pad' : [0, validate_float], # was 0.2, deprecated; the fractional whitespace inside the legend border - 'legend.borderpad' : [0.5, validate_float], # units are fontsize + 'legend.borderpad' : [0.4, validate_float], # units are fontsize 'legend.markerscale' : [1.0, validate_float], # the relative size of legend markers vs. original # the following dimensions are in axes coords @@ -433,6 +433,25 @@ 'legend.shadow' : [False, validate_bool], + 'legend.labelspacing' : [0.5, validate_float], # the vertical space between the legend entries + 'legend.handlelength' : [2., validate_float], # the length of the legend lines + 'legend.handletextpad' : [.8, validate_float], # the space between the legend line and legend text + 'legend.borderaxespad' : [0.5, validate_float], # the border between the axes and legend edge + 'legend.columnspacing' : [2., validate_float], # the border between the axes and legend edge + + + 'legend.markerscale' : [1.0, validate_float], # the relative size of legend markers vs. original + + # the following dimensions are in axes coords + 'legend.labelsep' : [0.010, validate_float], # the vertical space between the legend entries + 'legend.handlelen' : [0.05, validate_float], # the length of the legend lines + 'legend.handletextsep' : [0.02, validate_float], # the space between the legend line and legend text + 'legend.axespad' : [0.5, validate_float], # the border between the axes and legend edge + 'legend.shadow' : [False, validate_bool], + + + + # tick properties 'xtick.major.size' : [4, validate_float], # major xtick size in points 'xtick.minor.size' : [2, validate_float], # minor xtick size in points This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ds...@us...> - 2008-12-02 17:12:17
|
Revision: 6471 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6471&view=rev Author: dsdale Date: 2008-12-02 17:11:07 +0000 (Tue, 02 Dec 2008) Log Message: ----------- improved checks for external dependencies Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/__init__.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-02 17:07:57 UTC (rev 6470) +++ trunk/matplotlib/CHANGELOG 2008-12-02 17:11:07 UTC (rev 6471) @@ -1,3 +1,7 @@ +2008-12-02 Improve checks for external dependencies, using subprocess + (instead of deprecated popen*) and distutils (for version + checking) - DSD + 2008-11-30 Reimplementaion of the legend which supports baseline alignement, multi-column, and expand mode. - JJL Modified: trunk/matplotlib/lib/matplotlib/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/__init__.py 2008-12-02 17:07:57 UTC (rev 6470) +++ trunk/matplotlib/lib/matplotlib/__init__.py 2008-12-02 17:11:07 UTC (rev 6471) @@ -93,8 +93,9 @@ __revision__ = '$Revision$' __date__ = '$Date$' -import os, re, shutil, sys, warnings +import os, re, shutil, subprocess, sys, warnings import distutils.sysconfig +import distutils.version NEWCONFIG = False @@ -256,10 +257,10 @@ def checkdep_dvipng(): try: - stdin, stdout = os.popen4('dvipng -version') - line = stdout.readlines()[1] + s = subprocess.Popen(['dvipng','-version'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + line = s.stdout.readlines()[1] v = line.split()[-1] - float(v) return v except (IndexError, ValueError): return None @@ -267,47 +268,45 @@ def checkdep_ghostscript(): try: if sys.platform == 'win32': - command = 'gswin32c --version' + command_args = ['gswin32c', '--version'] else: - command = 'gs --version' - stdin, stdout = os.popen4(command) - v = stdout.read()[:-1] - vtest = '.'.join(v.split('.')[:2]) # deal with version numbers like '7.07.1' - float(vtest) - return vtest + command_args = ['gs', '--version'] + s = subprocess.Popen(command_args, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + v = s.stdout.read()[:-1] + return v except (IndexError, ValueError): return None def checkdep_tex(): try: - stdin, stdout = os.popen4('tex -version') - line = stdout.readlines()[0] + s = subprocess.Popen(['tex','-version'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + line = s.stdout.readlines()[0] pattern = '3\.1\d+' match = re.search(pattern, line) v = match.group(0) - float(v) return v except (IndexError, ValueError, AttributeError): return None def checkdep_pdftops(): try: - stdin, stdout = os.popen4('pdftops -v') - for line in stdout.readlines(): + s = subprocess.Popen(['pdftops','-v'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in s.stderr: if 'version' in line: v = line.split()[-1] - float(v) return v except (IndexError, ValueError, UnboundLocalError): return None def compare_versions(a, b): - "return True if a is greater than b" + "return True if a is greater than or equal to b" if a: - a = [int(i) for i in a.split('.')] - b = [int(i) for i in b.split('.')] - if a[0]>b[0]: return True - elif (a[0]==b[0]) and (a[1]>=b[1]): return True + a = distutils.version.LooseVersion(a) + b = distutils.version.LooseVersion(b) + if a>=b: return True else: return False else: return False @@ -330,8 +329,13 @@ if s == 'xpdf': pdftops_req = '3.0' + pdftops_req_alt = '0.9' # poppler version numbers, ugh pdftops_v = checkdep_pdftops() - if compare_versions(pdftops_v, pdftops_req): pass + if compare_versions(pdftops_v, pdftops_req): + pass + elif compare_versions(pdftops_v, pdftops_req_alt) and not \ + compare_versions(pdftops_v, '1.0'): + pass else: flag = False warnings.warn(('matplotlibrc ps.usedistiller can not be set to ' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-02 22:27:43
|
Revision: 6479 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6479&view=rev Author: leejjoon Date: 2008-12-02 22:27:38 +0000 (Tue, 02 Dec 2008) Log Message: ----------- Fixed a bug in the new legend class that didn't allowed a tuple of coordinate vlaues as loc Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/legend.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-02 22:04:41 UTC (rev 6478) +++ trunk/matplotlib/CHANGELOG 2008-12-02 22:27:38 UTC (rev 6479) @@ -1,3 +1,6 @@ +2008-12-02 Fixed a bug in the new legend class that didn't allowed + a tuple of coordinate vlaues as loc. -JJL + 2008-12-02 Improve checks for external dependencies, using subprocess (instead of deprecated popen*) and distutils (for version checking) - DSD Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-02 22:04:41 UTC (rev 6478) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-02 22:27:38 UTC (rev 6479) @@ -61,6 +61,9 @@ 'upper center' : 9, 'center' : 10, + loc can be a tuple of the noramilzed coordinate values with + respect its parent. + Return value is a sequence of text, line instances that make up the legend """ @@ -100,7 +103,7 @@ axespad = None, # deprecated; use borderaxespad # spacing & pad defined as a fractionof the font-size - borderpad = None, # the fractional whitespace inside the legend border + borderpad = None, # the whitespace inside the legend border labelspacing=None, #the vertical space between the legend entries handlelength=None, # the length of the legend handles handletextpad=None, # the pad between the legend handle and text @@ -119,11 +122,11 @@ Optional keyword arguments: - ================ ========================================= + ================ ================================================= Keyword Description - ================ ========================================= + ================ ================================================= - loc a location code + loc a location code or a tuple of coordinates numpoints the number of points in the legend line prop the font property markerscale the relative size of legend markers vs. original @@ -284,14 +287,22 @@ a.set_transform(self.get_transform()) def _findoffset_best(self, width, height, xdescent, ydescent): - "Heper function to locate the legend" + "Heper function to locate the legend at its best position" ox, oy = self._find_best_position(width, height) return ox+xdescent, oy+ydescent def _findoffset_loc(self, width, height, xdescent, ydescent): - "Heper function to locate the legend" - bbox = Bbox.from_bounds(0, 0, width, height) - x, y = self._get_anchored_bbox(self._loc, bbox, self.parent.bbox) + "Heper function to locate the legend using the location code" + + if iterable(self._loc) and len(self._loc)==2: + # when loc is a tuple of axes(or figure) coordinates. + fx, fy = self._loc + bbox = self.parent.bbox + x, y = bbox.x0 + bbox.width * fx, bbox.y0 + bbox.height * fy + else: + bbox = Bbox.from_bounds(0, 0, width, height) + x, y = self._get_anchored_bbox(self._loc, bbox, self.parent.bbox) + return x+xdescent, y+ydescent def draw(self, renderer): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-03 07:40:13
|
Revision: 6480 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6480&view=rev Author: leejjoon Date: 2008-12-03 07:40:09 +0000 (Wed, 03 Dec 2008) Log Message: ----------- reorganization of style classes in patches.py Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/patches.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-02 22:27:38 UTC (rev 6479) +++ trunk/matplotlib/CHANGELOG 2008-12-03 07:40:09 UTC (rev 6480) @@ -1,3 +1,7 @@ +2008-12-02 The transmuter classes in the patches.py are reorganized as + subclasses of the Style classes. A few more box and arrow + styles are added. -JJL + 2008-12-02 Fixed a bug in the new legend class that didn't allowed a tuple of coordinate vlaues as loc. -JJL Added: trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/fancyarrow_demo.py 2008-12-03 07:40:09 UTC (rev 6480) @@ -0,0 +1,43 @@ +import matplotlib.patches as mpatches +import matplotlib.pyplot as plt + +styles = mpatches.ArrowStyle.get_styles() + +figheight = (len(styles)+.5) +fig1 = plt.figure(1, (4, figheight)) +fontsize = 0.3 * fig1.dpi + +ax = plt.Axes(fig1, [0, 0, 1, 1], frameon=False, aspect=1.) +fig1.add_axes(ax) + +ax.set_xlim(0, 4) +ax.set_ylim(0, figheight) + +for i, (stylename, styleclass) in enumerate(sorted(styles.items())): + y = (float(len(styles)) -0.25 - i) # /figheight + p = mpatches.Circle((3.2, y), 0.2, fc="w") + ax.add_patch(p) + #ax.plot([0.8], [y], "o", mec="b", mfc="w", ms=20, transform=fig1.transFigure) + #ax.scatter([0.8], [y], s=20*20, marker="o", edgecolors=["b"], facecolors=["w"], + # ) + ax.annotate(stylename, (3.2, y), + (2., y), + #xycoords="figure fraction", textcoords="figure fraction", + ha="right", va="center", + size=fontsize, + arrowprops=dict(arrowstyle=stylename, + patchB=p, + shrinkA=5, + shrinkB=5, + fc="w", ec="k", + connectionstyle="arc3,rad=-0.05", + ), + bbox=dict(boxstyle="square", fc="w")) + +ax.xaxis.set_visible(False) +ax.yaxis.set_visible(False) + + + +plt.draw() +plt.show() Added: trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/fancybox_demo2.py 2008-12-03 07:40:09 UTC (rev 6480) @@ -0,0 +1,17 @@ +import matplotlib.patches as mpatch +import matplotlib.pyplot as plt + +styles = mpatch.BoxStyle.get_styles() + +figheight = (len(styles)+.5) +fig1 = plt.figure(1, (4, figheight)) +fontsize = 0.4 * fig1.dpi + +for i, (stylename, styleclass) in enumerate(styles.items()): + fig1.text(0.5, (float(len(styles)) - 0.5 - i)/figheight, stylename, + ha="center", + size=fontsize, + transform=fig1.transFigure, + bbox=dict(boxstyle=stylename, fc="w", ec="k")) +plt.draw() +plt.show() Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-12-02 22:27:38 UTC (rev 6479) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-12-03 07:40:09 UTC (rev 6480) @@ -289,6 +289,7 @@ rgbFace = (r, g, b) gc.set_alpha(a) + if self._hatch: gc.set_hatch(self._hatch ) @@ -1366,319 +1367,576 @@ r.draw(renderer) -class BboxTransmuterBase(object): + +def _pprint_table(_table, leadingspace=2): """ - :class:`BBoxTransmuterBase` and its derivatives are used to make a - fancy box around a given rectangle. The :meth:`__call__` method - returns the :class:`~matplotlib.path.Path` of the fancy box. This - class is not an artist and actual drawing of the fancy box is done - by the :class:`FancyBboxPatch` class. + Given the list of list of strings, return a string of REST table format. """ + if leadingspace: + pad = ' '*leadingspace + else: + pad = '' - # 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. + columns = [[] for cell in _table[0]] - def __init__(self): - super(BboxTransmuterBase, self).__init__() + for row in _table: + for column, cell in zip(columns, row): + column.append(cell) + + + col_len = [max([len(cell) for cell in column]) for column in columns] + lines = [] + table_formatstr = pad + ' '.join([('=' * cl) for cl in col_len]) + lines.append('') + lines.append(table_formatstr) + lines.append(pad + ' '.join([cell.ljust(cl) for cell, cl in zip(_table[0], col_len)])) + lines.append(table_formatstr) + + lines.extend([(pad + ' '.join([cell.ljust(cl) for cell, cl in zip(row, col_len)])) + for row in _table[1:]]) + + lines.append(table_formatstr) + lines.append('') + return "\n".join(lines) - def transmute(self, x0, y0, width, height, mutation_size): + +def _pprint_styles(_styles, leadingspace=2): + """ + A helper function for the _Style class. Given the dictionary of + (stylename : styleclass), return a formatted string listing all the + styles. Used to update the documentation. + """ + if leadingspace: + pad = ' '*leadingspace + else: + pad = '' + + names, attrss, clss = [], [], [] + + import inspect + + _table = [["Class", "Name", "Attrs"]] + + for name, cls in sorted(_styles.items()): + args, varargs, varkw, defaults = inspect.getargspec(cls.__init__) + if defaults: + args = [(argname, argdefault) \ + for argname, argdefault in zip(args[1:], defaults)] + else: + args = [] + + _table.append([cls.__name__, name, + ",".join([("%s=%s" % (an, av)) for an, av in args])]) + + return _pprint_table(_table) + + + +class _Style(object): + """ + A base class for the Styles. It is meant to be a container class, + where actual styles are declared as subclass of it, and it + provides some helper functions. + """ + def __new__(self, stylename, **kw): """ - The transmute method is a very core of the - :class:`BboxTransmuter` class and must be overriden in the - subclasses. It receives the location and size of the - rectangle, and the mutation_size, with which the amount of - padding and etc. will be scaled. It returns a - :class:`~matplotlib.path.Path` instance. + return the instance of the subclass with the given style name. """ - raise NotImplementedError('Derived must override') + # the "class" should have the _style_list attribute, which is + # a dictionary of stylname, style class paie. + + _list = stylename.replace(" ","").split(",") + _name = _list[0].lower() + try: + _cls = self._style_list[_name] + except KeyError: + raise ValueError("Unknown style : %s" % stylename) + try: + _args_pair = [cs.split("=") for cs in _list[1:]] + _args = dict([(k, float(v)) for k, v in _args_pair]) + except ValueError: + raise ValueError("Incorrect style argument : %s" % stylename) + _args.update(kw) - def __call__(self, x0, y0, width, height, mutation_size, - aspect_ratio=1.): + return _cls(**_args) + + + @classmethod + def get_styles(klass): """ - The __call__ method a thin wrapper around the transmute method - and take care of the aspect. + A class method which returns a dictionary of available styles. """ - if aspect_ratio is not None: - # Squeeze the given height by the aspect_ratio - y0, height = y0/aspect_ratio, height/aspect_ratio - # call transmute method with squeezed height. - path = self.transmute(x0, y0, width, height, mutation_size) - vertices, codes = path.vertices, path.codes - # Restore the height - vertices[:,1] = vertices[:,1] * aspect_ratio - return Path(vertices, codes) - else: - return self.transmute(x0, y0, width, height, mutation_size) + return klass._style_list + @classmethod + def pprint_styles(klass): + """ + A class method which returns a string of the available styles. + """ + return _pprint_styles(klass._style_list) -class SquareBoxTransmuter(BboxTransmuterBase): + + +class BoxStyle(_Style): """ - Simple square box. + :class:`BoxStyle` is a container class which defines several + boxstyle classes, which are used for :class:`FancyBoxPatch`. - *pad*: an amount of padding. + A style object can be created as + + BoxStyle.Round(pad=0.2) + + or + + BoxStyle("Round", pad=0.2) + + or + + BoxStyle("Round, pad=0.2") + + Following boxstyle classes are defined. + + %(AvailableBoxstyles)s + + An instance of any boxstyle class is an callable object, + whose call signature is + + __call__(self, x0, y0, width, height, mutation_size, aspect_ratio=1.) + + and returns a :class:`Path` instance. *x0*, *y0*, *width* and + *height* specify the location and size of the box to be + drawn. *mutation_scale* determines the overall size of the + mutation (by which I mean the transformation of the rectangle to + the fancy box). *mutation_aspect* determines the aspect-ratio of + the mutation. + """ + + _style_list = {} - def __init__(self, pad=0.3): - self.pad = pad - super(SquareBoxTransmuter, self).__init__() - def transmute(self, x0, y0, width, height, mutation_size): + class _Base(object): + """ + :class:`BBoxTransmuterBase` and its derivatives are used to make a + fancy box around a given rectangle. The :meth:`__call__` method + returns the :class:`~matplotlib.path.Path` of the fancy box. This + class is not an artist and actual drawing of the fancy box is done + by the :class:`FancyBboxPatch` class. + """ - # padding - pad = mutation_size * self.pad + # 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. - # width and height with padding added. - width, height = width + 2.*pad, \ - height + 2.*pad, + def __init__(self): + """ + initializtion. + """ + super(BoxStyle._Base, self).__init__() - # boundary of the padded box - x0, y0 = x0-pad, y0-pad, - x1, y1 = x0+width, y0 + height - cp = [(x0, y0), (x1, y0), (x1, y1), (x0, y1), - (x0, y0), (x0, y0)] - com = [Path.MOVETO, - Path.LINETO, - Path.LINETO, - Path.LINETO, - Path.LINETO, - Path.CLOSEPOLY] - path = Path(cp, com) + def transmute(self, x0, y0, width, height, mutation_size): + """ + The transmute method is a very core of the + :class:`BboxTransmuter` class and must be overriden in the + subclasses. It receives the location and size of the + rectangle, and the mutation_size, with which the amount of + padding and etc. will be scaled. It returns a + :class:`~matplotlib.path.Path` instance. + """ + raise NotImplementedError('Derived must override') - return path -class RoundBoxTransmuter(BboxTransmuterBase): - """ - A box with round corners. - """ + def __call__(self, x0, y0, width, height, mutation_size, + aspect_ratio=1.): + """ + Given the location and size of the box, return the path of + the box around it. - def __init__(self, pad=0.3, rounding_size=None): - self.pad = pad - self.rounding_size = rounding_size - BboxTransmuterBase.__init__(self) + - *x0*, *y0*, *width*, *height* : location and size of the box + - *mutation_size* : a reference scale for the mutation. + - *aspect_ratio* : aspect-ration for the mutation. + """ + # The __call__ method is a thin wrapper around the transmute method + # and take care of the aspect. - def transmute(self, x0, y0, width, height, mutation_size): + if aspect_ratio is not None: + # Squeeze the given height by the aspect_ratio + y0, height = y0/aspect_ratio, height/aspect_ratio + # call transmute method with squeezed height. + path = self.transmute(x0, y0, width, height, mutation_size) + vertices, codes = path.vertices, path.codes + # Restore the height + vertices[:,1] = vertices[:,1] * aspect_ratio + return Path(vertices, codes) + else: + return self.transmute(x0, y0, width, height, mutation_size) - # padding - pad = mutation_size * self.pad - # size of the roudning corner - if self.rounding_size: - dr = mutation_size * self.rounding_size - else: - dr = pad - width, height = width + 2.*pad, \ - height + 2.*pad, + class Square(_Base): + """ + A simple square box. + """ + def __init__(self, pad=0.3): + """ + *pad* + amount of padding + """ + + self.pad = pad + super(BoxStyle.Square, self).__init__() - x0, y0 = x0-pad, y0-pad, - x1, y1 = x0+width, y0 + height + def transmute(self, x0, y0, width, height, mutation_size): - # Round corners are implemented as quadratic bezier. eg. - # [(x0, y0-dr), (x0, y0), (x0+dr, y0)] for lower left corner. - cp = [(x0+dr, y0), - (x1-dr, y0), - (x1, y0), (x1, y0+dr), - (x1, y1-dr), - (x1, y1), (x1-dr, y1), - (x0+dr, y1), - (x0, y1), (x0, y1-dr), - (x0, y0+dr), - (x0, y0), (x0+dr, y0), - (x0+dr, y0)] + # padding + pad = mutation_size * self.pad - com = [Path.MOVETO, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.CLOSEPOLY] + # width and height with padding added. + width, height = width + 2.*pad, \ + height + 2.*pad, - path = Path(cp, com) + # boundary of the padded box + x0, y0 = x0-pad, y0-pad, + x1, y1 = x0+width, y0 + height - return path + cp = [(x0, y0), (x1, y0), (x1, y1), (x0, y1), + (x0, y0), (x0, y0)] + com = [Path.MOVETO, + Path.LINETO, + Path.LINETO, + Path.LINETO, + Path.LINETO, + Path.CLOSEPOLY] -class Round4BoxTransmuter(BboxTransmuterBase): - """ - A box with round edges. - """ + path = Path(cp, com) - def __init__(self, pad=0.3, rounding_size=None): - self.pad = pad - self.rounding_size = rounding_size - BboxTransmuterBase.__init__(self) + return path - def transmute(self, x0, y0, width, height, mutation_size): + _style_list["square"] = Square - # 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. + class LArrow(_Base): + """ + (left) Arrow Box + """ - width, height = width + 2.*pad - 2*dr, \ - height + 2.*pad - 2*dr, + def __init__(self, pad=0.3): + self.pad = pad + super(BoxStyle.LArrow, self).__init__() + def transmute(self, x0, y0, width, height, mutation_size): - x0, y0 = x0-pad+dr, y0-pad+dr, - x1, y1 = x0+width, y0 + height + # padding + pad = mutation_size * self.pad + # width and height with padding added. + width, height = width + 2.*pad, \ + height + 2.*pad, - 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)] + # boundary of the padded box + x0, y0 = x0-pad, y0-pad, + x1, y1 = x0+width, y0 + height - 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] + dx = (y1-y0)/2. + dxx = dx*.5 + # adjust x0. 1.4 <- sqrt(2) + x0 = x0 + pad / 1.4 + + cp = [(x0+dxx, y0), (x1, y0), (x1, y1), (x0+dxx, y1), + (x0+dxx, y1+dxx), (x0-dx, y0+dx), (x0+dxx, y0-dxx), # arrow + (x0+dxx, y0), (x0+dxx, y0)] - path = Path(cp, com) + com = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.LINETO, Path.LINETO, + Path.LINETO, Path.CLOSEPOLY] - return path + path = Path(cp, com) + return path + _style_list["larrow"] = LArrow -class SawtoothBoxTransmuter(BboxTransmuterBase): - """ - A sawtooth box. - """ + class RArrow(LArrow): + """ + (right) Arrow Box + """ - def __init__(self, pad=0.3, tooth_size=None): - self.pad = pad - self.tooth_size = tooth_size - BboxTransmuterBase.__init__(self) + def __init__(self, pad=0.3): + self.pad = pad + super(BoxStyle.RArrow, self).__init__() - def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): + def transmute(self, x0, y0, width, height, mutation_size): + p = BoxStyle.LArrow.transmute(self, x0, y0, + width, height, mutation_size) - # padding - pad = mutation_size * self.pad + p.vertices[:,0] = 2*x0 + width - p.vertices[:,0] - # size of sawtooth - if self.tooth_size is None: - tooth_size = self.pad * .5 * mutation_size - else: - tooth_size = self.tooth_size * mutation_size + return p - tooth_size2 = tooth_size / 2. - width, height = width + 2.*pad - tooth_size, \ - height + 2.*pad - tooth_size, + + _style_list["rarrow"] = RArrow - # 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 + class Round(_Base): + """ + A box with round corners. + """ - x0, y0 = x0-pad+tooth_size2, y0-pad+tooth_size2 - x1, y1 = x0+width, y0 + height + def __init__(self, pad=0.3, rounding_size=None): + """ + *pad* + amount of padding + *rounding_size* + rounding radius of corners. *pad* if None + """ + self.pad = pad + self.rounding_size = rounding_size + super(BoxStyle.Round, self).__init__() - 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] + def transmute(self, x0, y0, width, height, mutation_size): - 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] + # padding + pad = mutation_size * self.pad - 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] + # size of the roudning corner + if self.rounding_size: + dr = mutation_size * self.rounding_size + else: + dr = pad - 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] + width, height = width + 2.*pad, \ + height + 2.*pad, - 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 + x0, y0 = x0-pad, y0-pad, + x1, y1 = x0+width, y0 + height + # Round corners are implemented as quadratic bezier. eg. + # [(x0, y0-dr), (x0, y0), (x0+dr, y0)] for lower left corner. + cp = [(x0+dr, y0), + (x1-dr, y0), + (x1, y0), (x1, y0+dr), + (x1, y1-dr), + (x1, y1), (x1-dr, y1), + (x0+dr, y1), + (x0, y1), (x0, y1-dr), + (x0, y0+dr), + (x0, y0), (x0+dr, y0), + (x0+dr, y0)] - def transmute(self, x0, y0, width, height, mutation_size): + com = [Path.MOVETO, + Path.LINETO, + Path.CURVE3, Path.CURVE3, + Path.LINETO, + Path.CURVE3, Path.CURVE3, + Path.LINETO, + Path.CURVE3, Path.CURVE3, + Path.LINETO, + Path.CURVE3, Path.CURVE3, + Path.CLOSEPOLY] - saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) - path = Path(saw_vertices) - return path + path = Path(cp, com) + return path -class RoundtoothBoxTransmuter(SawtoothBoxTransmuter): - """ - A roundtooth(?) box. - """ + _style_list["round"] = Round - def transmute(self, x0, y0, width, height, mutation_size): - saw_vertices = self._get_sawtooth_vertices(x0, y0, width, height, mutation_size) + class Round4(_Base): + """ + Another box with round edges. + """ - cp = [Path.MOVETO] + ([Path.CURVE3, Path.CURVE3] * ((len(saw_vertices)-1)//2)) - path = Path(saw_vertices, cp) + def __init__(self, pad=0.3, rounding_size=None): + """ + *pad* + amount of padding - return path + *rounding_size* + rounding size of edges. *pad* if None + """ + + self.pad = pad + self.rounding_size = rounding_size + super(BoxStyle.Round4, self).__init__() + def transmute(self, x0, y0, width, height, mutation_size): -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 - 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)) - return s + # 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 + + _style_list["round4"] = Round4 + + + class Sawtooth(_Base): + """ + A sawtooth box. + """ + + def __init__(self, pad=0.3, tooth_size=None): + """ + *pad* + amount of padding + + *tooth_size* + size of the sawtooth. pad* if None + """ + self.pad = pad + self.tooth_size = tooth_size + super(BoxStyle.Sawtooth, self).__init__() + + 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 + + _style_list["sawtooth"] = Sawtooth + + + class Roundtooth(Sawtooth): + """ + A roundtooth(?) box. + """ + + def __init__(self, pad=0.3, tooth_size=None): + """ + *pad* + amount of padding + + *tooth_size* + size of the sawtooth. pad* if None + """ + super(BoxStyle.Roundtooth, self).__init__(pad, tooth_size) + + + 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 + + _style_list["roundtooth"] = Roundtooth + + __doc__ = cbook.dedent(__doc__) % \ + {"AvailableBoxstyles": _pprint_styles(_style_list)} + + class FancyBboxPatch(Patch): """ Draw a fancy box around a rectangle with lower left at *xy*=(*x*, @@ -1689,27 +1947,8 @@ transformation of the rectangle box to the fancy box is delegated to the :class:`BoxTransmuterBase` and its derived classes. - *boxstyle* determines what kind of fancy box will be drawn. In - other words, it selects the :class:`BboxTransmuter` class to use, - and sets optional attributes. - - *bbox_transmuter* can specify a custom :class:`BboxTransmuter` - instance. - - *mutation_scale* determines the overall size of the mutation (by - which I mean the transformation of the rectangle to the fancy - path) - - *mutation_aspect* determines the aspect-ratio of the mutation. """ - _fancy_bbox_transmuters = {"square":SquareBoxTransmuter, - "round":RoundBoxTransmuter, - "round4":Round4BoxTransmuter, - "sawtooth":SawtoothBoxTransmuter, - "roundtooth":RoundtoothBoxTransmuter, - } - def __str__(self): return self.__class__.__name__ \ + "FancyBboxPatch(%g,%g;%gx%g)" % (self._x, self._y, self._width, self._height) @@ -1725,18 +1964,13 @@ *width*, *height* - *boxstyle* describes how the fancy box will be drawn. It - should be one of the available boxstyle names, with optional - comma-separated attributes. These attributes are meant to be - scaled with the *mutation_scale*. Following box styles are - available. + *boxstyle* determines what kind of fancy box will be drawn. It + can be a string of the style name with a comma separated + attribute, or an instance of :class:`BoxStyle`. Following box + styles are available. %(AvailableBoxstyles)s - The *boxstyle* name can be "custom", in which case the - *bbox_transmuter* argument needs to be set, which should be an - instance of :class:`BboxTransmuterBase` (or its derived). - *mutation_scale* : a value with which attributes of boxstyle (e.g., pad) will be scaled. default=1. @@ -1767,17 +2001,12 @@ kwdoc = dict() - kwdoc["AvailableBoxstyles"]="\n".join([" - " + l \ - for l in _list_available_boxstyles(_fancy_bbox_transmuters)]) + kwdoc["AvailableBoxstyles"]=_pprint_styles(BoxStyle._style_list) kwdoc.update(artist.kwdocd) __init__.__doc__ = cbook.dedent(__init__.__doc__) % kwdoc del kwdoc - @classmethod - def list_available_boxstyles(cls): - return _list_available_boxstyles(cls._fancy_bbox_transmuters) - def set_boxstyle(self, boxstyle=None, **kw): """ Set the box style. @@ -1799,29 +2028,20 @@ if boxstyle==None: # print out available boxstyles and return. - print " Following box styles are available." - for l in self.list_available_boxstyles(): - print " - " + l + print "Following box styles are available:" + print BoxStyle.pprint_styles() return - # parse the boxstyle descrption (e.g. "round,pad=0.3") - bs_list = boxstyle.replace(" ","").split(",") - boxstyle_name = bs_list[0] - try: - bbox_transmuter_cls = self._fancy_bbox_transmuters[boxstyle_name] - except KeyError: - raise ValueError("Unknown Boxstyle : %s" % boxstyle_name) - try: - boxstyle_args_pair = [bs.split("=") for bs in bs_list[1:]] - boxstyle_args = dict([(k, float(v)) for k, v in boxstyle_args_pair]) - except ValueError: - raise ValueError("Incorrect Boxstyle argument : %s" % boxstyle) + if isinstance(boxstyle, BoxStyle._Base): + self._bbox_transmuter = boxstyle + elif callable(boxstyle): + self._bbox_transmuter = boxstyle + else: + self._bbox_transmuter = BoxStyle(boxstyle, **kw) + - boxstyle_args.update(kw) - self._bbox_transmuter = bbox_transmuter_cls(**boxstyle_args) kwdoc = dict() - kwdoc["AvailableBoxstyles"]=" | ".join([l \ - for l in _list_available_boxstyles(_fancy_bbox_transmuters)]) + kwdoc["AvailableBoxstyles"]=_pprint_styles(BoxStyle._style_list) kwdoc.update(artist.kwdocd) set_boxstyle.__doc__ = cbook.dedent(set_boxstyle.__doc__) % kwdoc del kwdoc @@ -1854,16 +2074,8 @@ """ return self._mutation_aspect - def set_bbox_transmuter(self, bbox_transmuter): - """ - Set the transmuter object - - ACCEPTS: :class:`BboxTransmuterBase` (or its derivatives) instance - """ - self._bbox_transmuter = bbox_transmuter - - def get_bbox_transmuter(self): - "Return the current transmuter object" + def get_boxstyle(self): + "Return the boxstyle object" return self._bbox_transmuter def get_path(self): @@ -1871,10 +2083,10 @@ Return the mutated path of the rectangle """ - _path = self.get_bbox_transmuter()(self._x, self._y, - self._width, self._height, - self.get_mutation_scale(), - self.get_mutation_aspect()) + _path = self.get_boxstyle()(self._x, self._y, + self._width, self._height, + self.get_mutation_scale(), + self.get_mutation_aspect()) return _path @@ -1954,239 +2166,354 @@ 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 +from matplotlib.bezier import split_path_inout, inside_circle, get_cos_sin -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 ConnectionStyle(_Style): """ + :class:`ConnectionStyle` is a container class which defines + several connectionstyle classes, which is used to create a path + between two points. These are mainly used with + :class:`FancyArrowPatch`. - class SimpleEvent: - def __init__(self, xy): - self.x, self.y = xy + A connectionstyle object can be either created as - 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. + ConnectionStyle.Arc3(rad=0.2) + + or + + ConnectionStyle("Arc3", rad=0.2) + + or + + ConnectionStyle("Arc3, rad=0.2") + + Following classes are defined + + %(AvailableConnectorstyles)s + + + An instance of any connection style class is an callable object, + whose call signature is + + __call__(self, posA, posB, patchA=None, patchB=None, shrinkA=2., shrinkB=2.) + + and it returns a :class:`Path` instance. *posA* and *posB are tuples + of x,y coordinates of the two points to be connected. *patchA* (or + $patchB*) is given, the returned path is clipped so that it start + (or end) from the boundary of the patch. The path is further + shrinked by *shrinkA* (or *shrinkB*) which is given in points. + """ + + _style_list = {} + + + class _Base(object): """ + A base class for connectionstyle classes. The dervided needs + to implement a *connect* methods whose call signature is - 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] + connect(posA, posB) + + where posA and posB are tuples of x, y coordinates to be + connected. The methods needs to return a path connecting two + points. This base class defines a __call__ method, and few + helper methods. + """ - try: + 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 = ConnectionStyle._Base.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 = ConnectionStyle._Base.SimpleEvent(xy_display) + return patchB.contains(xy_event)[0] + + try: + left, right = split_path_inout(path, insideB) + except ValueError: + left = path + + path = left + + 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) - except ValueError: - right = path + path = right - path = right + if shrinkB: + x, y = path.vertices[-1] + insideB = inside_circle(x, y, shrinkB) - 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 - path = left + return path - #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] + def __call__(self, posA, posB, + shrinkA=2., shrinkB=2., patchA=None, patchB=None): + """ + Calls the *connect* method to create a path between *posA* + and *posB*. The path is clipped and shrinked. + """ + + path = self.connect(posA, posB) - return path + clipped_path = self._clip(path, patchA, patchB) + shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) + return shrinked_path - def _shrink(self, path, shrinkA, shrinkB): + + class Arc3(_Base): """ - Shrink the path by fixed size (in points) with shrinkA and shrinkB + 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. """ - if shrinkA: - x, y = path.vertices[0] - insideA = inside_circle(x, y, shrinkA) - left, right = split_path_inout(path, insideA) - path = right + def __init__(self, rad=0.): + """ + *rad* + curvature of the curve. + """ + self.rad = rad - if shrinkB: - x, y = path.vertices[-1] - insideB = inside_circle(x, y, shrinkB) + 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 - left, right = split_path_inout(path, insideB) - path = left + f = self.rad - return path + cx, cy = x12 + f*dy, y12 - f*dx - def __call__(self, posA, posB, - shrinkA=2., shrinkB=2., patchA=None, patchB=None): + vertices = [(x1, y1), + (cx, cy), + (x2, y2)] + codes = [Path.MOVETO, + Path.CURVE3, + Path.CURVE3] - path = self.connect(posA, posB) + return Path(vertices, codes) - clipped_path = self._clip(path, patchA, patchB) - shrinked_path = self._shrink(clipped_path, shrinkA, shrinkB) + _style_list["arc3"] = Arc3 + - return shrinked_path + class Angle3(_Base): + """ + 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). + """ -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 __init__(self, angleA=90, angleB=0): + """ + *angleA* + starting angle of the path - 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 + *angleB* + ending angle of the path + """ - f = self.rad + self.angleA = angleA + self.angleB = angleB - cx, cy = x12 + f*dy, y12 - f*dx - vertices = [(x1, y1), - (cx, cy), - (x2, y2)] - codes = [Path.MOVETO, - Path.CURVE3, - Path.CURVE3] + def connect(self, posA, posB): + x1, y1 = posA + x2, y2 = posB - return Path(vertices, codes) + 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) -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 + vertices = [(x1, y1), (cx, cy), (x2, y2)] + codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB + return Path(vertices, codes) - 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), + _style_list["angle3"] = Angle3 - 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] + class Angle(_Base): + """ + 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*. + """ - return Path(vertices, codes) + def __init__(self, angleA=90, angleB=0, rad=0.): + """ + *angleA* + starting angle of the path + *angleB* + ending angle of the path -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*. - """ + *rad* + rounding radius of the edge + """ - def __init__(self, angleA=90, angleB=0, rad=0.): - self.angleA = angleA - self.angleB = angleB + self.angleA = angleA + self.angleB = angleB - self.rad = rad + self.rad = rad - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB + 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), + 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) + cx, cy = get_intersection(x1, y1, cosA, sinA, + x2, y2, cosB, sinB) - vertices = [(x1, y1)] - codes = [Path.MOVETO] + vertices = [(x1, y1)] + codes = [Path.MOVETO] - if self.rad == 0.: - vertices.append((cx, cy)) + 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) - 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) - return Path(vertices, codes) + _style_list["angle"] = Angle + class Arc(_Base): + """ + 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*. + """ -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.): + """ + *angleA* : + starting angle of the path - 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 + *angleB* : + ending angle of the path - self.rad = rad + *armA* : + length of the starting arm - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB + *armB* : + length of the ending arm - vertices = [(x1, y1)] - rounded = [] - codes = [Path.MOVETO] + *rad* : + rounding radius of the edges + """ - 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)) + self.angleA = angleA + self.angleB = angleB + self.armA = armA + self.armB = armB - 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 + 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 = x_armB - xp, y_armB - yp + dx, dy = x2 - xp, y2 - yp dd = (dx*dx + dy*dy)**.5 rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) @@ -2194,528 +2521,677 @@ 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)] + vertices.append((x2, y2)) + codes.append(Path.LINETO) - if rounded: - xp, yp = rounded[-1] - dx, dy = x2 - xp, y2 - yp - dd = (dx*dx + dy*dy)**.5 + return Path(vertices, codes) - rounded.append((xp + self.rad*dx/dd, yp + self.rad*dy/dd)) - vertices.extend(rounded) - codes.extend([Path.LINETO, - Path.CURVE3, - Path.CURVE3]) + _style_list["arc"] = Arc - vertices.append((x2, y2)) - codes.append(Path.LINETO) + __doc__ = cbook.dedent(__doc__) % \ + {"AvailableConnectorstyles": _pprint_styles(_style_list)} + - return Path(vertices, codes) +class ArrowStyle(_Style): + """ + :class:`ArrowStyle` is a container class which defines several + arrowstyle classes, which is used to create an arrow path along a + given path. These are mainly used with :class:`FancyArrowPatch`. + A arrowstyle object can be either created as + ArrowStyle.Fancy(head_length=.4, head_width=.4, tail_width=.4) + or -class ArrowTransmuterBase(object): - """ - Arrow Transmuter Base class + ArrowStyle("Fancy", head_length=.4, head_width=.4, tail_width=.4) - 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. + or + + ArrowStyle("Fancy, head_length=.4, head_width=.4, tail_width=.4") + Following classes are defined + + %(AvailableArrowstyles)s + + + An instance of any arrow style class is an callable object, + whose call signature is + + __call__(self, path, mutation_size, linewidth, aspect_ratio=1.) + + and it returns a tuple of a :class:`Path` instance and a boolean + value. *path* is a :class:`Path` instance along witch the arrow + will be drawn. *mutation_size* and *aspect_ratio* has a same + meaning as in :class:`BoxStyle`. *linewidth* is a line width to be + stroked. This is meant to be used to correct the location of the + head so that it does not overshoot the destination point, but not all + classes support it. """ - # 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__() + _style_list = {} - @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. + class _Base(object): """ - segments = list(path.iter_segments()) - assert len(segments) == 2 + Arrow Transmuter Base class - assert segments[0][1] == Path.MOVETO - assert segments[1][1] == Path.CURVE3 + 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. - return list(segments[0][0]) + list(segments[1][0]) + """ + # 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 transmute(self, path, mutation_size, linewidth): + def __init__(self): + super(ArrowStyle.... [truncated message content] |
From: <as...@us...> - 2008-12-04 18:49:20
|
Revision: 6488 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6488&view=rev Author: astraw Date: 2008-12-04 18:49:16 +0000 (Thu, 04 Dec 2008) Log Message: ----------- git documentation: remove README.git and add to FAQ Modified Paths: -------------- trunk/matplotlib/doc/faq/installing_faq.rst Removed Paths: ------------- trunk/matplotlib/README.git Deleted: trunk/matplotlib/README.git =================================================================== --- trunk/matplotlib/README.git 2008-12-04 01:58:58 UTC (rev 6487) +++ trunk/matplotlib/README.git 2008-12-04 18:49:16 UTC (rev 6488) @@ -1,29 +0,0 @@ -There is an experimental `matplotlib github mirror`_ of the subversion -repository. To make a local clone it the directory ``mpl.git``, enter -the following commands. These instructions assume you already have a -github login (-- they use the personal gi...@gi... clone URL -instead of the git://github.com clone URL):: - - # This will create your copy in the mpl.git directory - git clone gi...@gi...:astraw/matplotlib.git mpl.git - cd mpl.git - git config --add remote.origin.fetch +refs/remotes/*:refs/remotes/* - git fetch - git svn init --trunk=trunk/matplotlib --tags=tags https://matplotlib.svn.sourceforge.net/svnroot/matplotlib - - # Now just get the latest svn revisions from the SourceForge SVN repository - git svn fetch -r 6300:HEAD - -.. _matplotlib github mirror: http://github.com/astraw/matplotlib - -To update your git repository with the latest svn updates from SourceForge:: - - git svn rebase - -To list what changes will be committed to svn:: - - git svn dcommit -n - -To commit your changes to svn:: - - git svn dcommit Modified: trunk/matplotlib/doc/faq/installing_faq.rst =================================================================== --- trunk/matplotlib/doc/faq/installing_faq.rst 2008-12-04 01:58:58 UTC (rev 6487) +++ trunk/matplotlib/doc/faq/installing_faq.rst 2008-12-04 18:49:16 UTC (rev 6488) @@ -106,8 +106,50 @@ > cd matplotlib > python setup.py install +Install from git +================ +There is an experimental `matplotlib github mirror`_ of the subversion +repository. To make a local clone it the directory ``mpl.git``, enter +the following commands:: + # This will create your copy in the mpl.git directory + git clone git://github.com/astraw/matplotlib.git mpl.git + cd mpl.git + git config --add remote.origin.fetch +refs/remotes/*:refs/remotes/* + git fetch + git svn init --trunk=trunk/matplotlib --tags=tags https://matplotlib.svn.sourceforge.net/svnroot/matplotlib + + # Now just get the latest svn revisions from the SourceForge SVN repository + git svn fetch -r 6300:HEAD + +.. _matplotlib github mirror: http://github.com/astraw/matplotlib + +To update your git repository with the latest svn updates from SourceForge:: + + git svn rebase + +To list what changes will be committed to svn:: + + git svn dcommit -n + +To commit your changes to svn:: + + git svn dcommit + +A note about git write access +----------------------------- + +The matplotlib developers need to figure out if there should be write +access to the git repository. This implies using the personal URL +(``gi...@gi...:astraw/matplotlib.git``) rather than the public URL +(``git://github.com/astraw/matplotlib.git``) for the +repository. However, doing so may make life complicated in the sense +that then there are two writeable matplotlib repositories, which must +be synced to prevent divergence. This is probably not an +insurmountable problem, but it is a problem that the developers should +reach a consensus about. Watch this space... + Backends ======== This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-05 00:06:31
|
Revision: 6494 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6494&view=rev Author: leejjoon Date: 2008-12-05 00:06:26 +0000 (Fri, 05 Dec 2008) Log Message: ----------- some minor changes for the legend Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/examples/pylab_examples/legend_demo3.py trunk/matplotlib/lib/matplotlib/legend.py trunk/matplotlib/lib/matplotlib/offsetbox.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-04 22:13:57 UTC (rev 6493) +++ trunk/matplotlib/CHANGELOG 2008-12-05 00:06:26 UTC (rev 6494) @@ -1,3 +1,7 @@ +2008-12-04 Added fancybox keyword to legend. Also applied some changes + for better look, including baseline adjustment of the + multiline texts so that it is center aligned. -JJL + 2008-12-02 The transmuter classes in the patches.py are reorganized as subclasses of the Style classes. A few more box and arrow styles are added. -JJL Modified: trunk/matplotlib/examples/pylab_examples/legend_demo3.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2008-12-04 22:13:57 UTC (rev 6493) +++ trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2008-12-05 00:06:26 UTC (rev 6494) @@ -2,6 +2,7 @@ import matplotlib.pyplot as plt import numpy as np +import pylab def myplot(ax): t1 = np.arange(0.0, 1.0, 0.1) @@ -21,7 +22,7 @@ ax3 = plt.subplot(3,1,3) myplot(ax3) -ax3.legend(loc=1, ncol=4, mode="expand", shadow=True) +ax3.legend(loc=1, ncol=4, mode="expand", fancybox=False, shadow=True) #title('Damped oscillation') Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-04 22:13:57 UTC (rev 6493) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-05 00:06:26 UTC (rev 6494) @@ -113,6 +113,7 @@ ncol=1, # number of columns mode=None, # mode for horizontal distribution of columns. None, "expand" + fancybox=True, shadow = None, ): """ @@ -130,6 +131,7 @@ numpoints the number of points in the legend line prop the font property markerscale the relative size of legend markers vs. original + fancybox if True, draw a frame with a round fancybox. shadow if True, draw a shadow behind legend scatteryoffsets a list of yoffsets for scatter symbols in legend @@ -263,8 +265,11 @@ # The width and height of the legendPatch will be set (in the # draw()) to the length that includes the padding. Thus we set # pad=0 here. - self.legendPatch.set_boxstyle("round",pad=0, #self.borderpad, - rounding_size=0.2) + if fancybox == True: + self.legendPatch.set_boxstyle("round",pad=0, + rounding_size=0.2) + else: + self.legendPatch.set_boxstyle("square",pad=0) self._set_artist_props(self.legendPatch) @@ -378,7 +383,8 @@ labelboxes = [] for l in labels: - textbox = TextArea(l, textprops=label_prop) + textbox = TextArea(l, textprops=label_prop + multilinebaseline=True, minimumdescent=True) text_list.append(textbox._text) labelboxes.append(textbox) @@ -387,8 +393,8 @@ # The approximate height and descent of text. These values are # only used for plotting the legend handle. - height = self._approx_text_height() * 0.6 - descent = 0. #height/6. + height = self._approx_text_height() * 0.7 + descent = 0. # each handle needs to be drawn inside a box of # (x, y, w, h) = (0, -descent, width, height). @@ -440,9 +446,9 @@ legline._legmarker = legline_marker elif isinstance(handle, Patch): - p = Rectangle(xy=(0, -0.*descent), + p = Rectangle(xy=(0., 0.), width = self.handlelength*self.fontsize, - height=0.*descent+(height-descent)*.9, + height=(height-descent), ) p.update_from(handle) self._set_artist_props(p) @@ -513,12 +519,12 @@ num_smallcol = self._ncol-num_largecol # starting index of each column and number of rows in it. - largecol = zip(range(0, num_largecol*(nrows+1), (nrows+1)), - [nrows+1] * num_largecol) - smallcol = zip(range(num_largecol*(nrows+1), len(handleboxes), nrows), - [nrows] * num_smallcol) + largecol = safezip(range(0, num_largecol*(nrows+1), (nrows+1)), + [nrows+1] * num_largecol) + smallcol = safezip(range(num_largecol*(nrows+1), len(handleboxes), nrows), + [nrows] * num_smallcol) - handle_label = zip(handleboxes, labelboxes) + handle_label = safezip(handleboxes, labelboxes) columnbox = [] for i0, di in largecol+smallcol: # pack handleBox and labelBox into itemBox @@ -526,6 +532,8 @@ sep=self.handletextpad*self.fontsize, children=[h, t], align="baseline") for h, t in handle_label[i0:i0+di]] + # minimumdescent=False for the text of the last row of the column + itemBoxes[-1].get_children()[1].set_minimumdescent(False) # pack columnBox columnbox.append(VPacker(pad=0, Modified: trunk/matplotlib/lib/matplotlib/offsetbox.py =================================================================== --- trunk/matplotlib/lib/matplotlib/offsetbox.py 2008-12-04 22:13:57 UTC (rev 6493) +++ trunk/matplotlib/lib/matplotlib/offsetbox.py 2008-12-05 00:06:26 UTC (rev 6494) @@ -23,9 +23,9 @@ from matplotlib.patches import bbox_artist as mbbox_artist DEBUG=False # for debuging use -def bbox_artist(*kl, **kw): +def bbox_artist(*args, **kwargs): if DEBUG: - mbbox_artist(*kl, **kw) + mbbox_artist(*args, **kwargs) # _get_packed_offsets() and _get_aligned_offsets() are coded assuming @@ -122,9 +122,9 @@ The OffsetBox is a simple container artist. The child artist are meant to be drawn at a relative position to its parent. """ - def __init__(self, *kl, **kw): + def __init__(self, *args, **kwargs): - super(OffsetBox, self).__init__(*kl, **kw) + super(OffsetBox, self).__init__(*args, **kwargs) self._children = [] self._offset = (0, 0) @@ -441,10 +441,18 @@ - def __init__(self, s, textprops=None, **kw): + def __init__(self, s, + textprops=None, + multilinebaseline=None, + minimumdescent=True, + ): """ - *s* : a string to be displayer. - *trnaspose* : transformation matrrix + *s* : a string to be displayed. + *textprops* : property dictionary for the text + *multilinebaseline* : If True, baseline for multiline text is + adjusted so that it is (approximatedly) + center-aligned with singleline text. + *minimumdescent* : If True, the box has a minimum descent of "p". """ if textprops is None: textprops = {} @@ -462,9 +470,48 @@ self.offset_transform = mtransforms.Affine2D() self.offset_transform.clear() self.offset_transform.translate(0, 0) - self._text.set_transform(self.offset_transform) + self._baseline_transform = mtransforms.Affine2D() + self._text.set_transform(self.offset_transform+self._baseline_transform) + self._multilinebaseline = multilinebaseline + self._minimumdescent = minimumdescent + + def set_multilinebaseline(self, t): + """ + Set multilinebaseline . + + If True, baseline for multiline text is + adjusted so that it is (approximatedly) center-aligned with + singleline text. + """ + self._multilinebaseline = t + + + def get_multilinebaseline(self): + """ + get multilinebaseline . + """ + return self._multilinebaseline + + + def set_minimumdescent(self, t): + """ + Set minimumdescent . + + If True, extent of the single line text is adjusted so that + it has minimum descent of "p" + """ + self._minimumdescent = t + + + def get_minimumdescent(self): + """ + get minimumdescent. + """ + return self._minimumdescent + + def set_transform(self, t): """ set_transform is ignored. @@ -507,17 +554,34 @@ bbox, info = self._text._get_layout(renderer) w, h = bbox.width, bbox.height + line = info[0][0] # first line _, hh, dd = renderer.get_text_width_height_descent( line, self._text._fontproperties, ismath=ismath) - d = h-(hh-dd) # the baseline of the first line - # for multiple lines, h or d may greater than h_ or d_. - h_d = max(h_ - d_, h-d) - d = max(d, d_) - h = h_d + d + self._baseline_transform.clear() + if len(info) > 1 and self._multilinebaseline: # multi line + d = h-(hh-dd) # the baseline of the first line + d_new = 0.5 * h - 0.5 * (h_ - d_) + + self._baseline_transform.translate(0, d - d_new) + d = d_new + + else: # single line + + h_d = max(h_ - d_, h-dd) + + if self.get_minimumdescent(): + ## to have a minimum descent, #i.e., "l" and "p" have same + ## descents. + d = max(dd, d_) + else: + d = dd + + h = h_d + d + return w, h, 0., d This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-05 21:09:45
|
Revision: 6499 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6499&view=rev Author: leejjoon Date: 2008-12-05 21:09:42 +0000 (Fri, 05 Dec 2008) Log Message: ----------- Fixed a handlelegth bug in the legend class Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/legend.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-05 19:30:28 UTC (rev 6498) +++ trunk/matplotlib/CHANGELOG 2008-12-05 21:09:42 UTC (rev 6499) @@ -1,3 +1,6 @@ +2008-12-05 Fixed a bug that the handlelength of the new legend class + set too short when numpoints=1 -JJL + 2008-12-04 Added support for data with units (e.g. dates) to Axes.fill_between. -RM Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-05 19:30:28 UTC (rev 6498) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-05 21:09:42 UTC (rev 6499) @@ -418,7 +418,7 @@ npoints) xdata_marker = xdata elif npoints == 1: - xdata = np.linspace(0, self.handlelength, 2) + xdata = np.linspace(0, self.handlelength*self.fontsize, 2) xdata_marker = [0.5*self.handlelength*self.fontsize] if isinstance(handle, Line2D): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-06 23:17:16
|
Revision: 6502 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6502&view=rev Author: leejjoon Date: 2008-12-06 23:17:10 +0000 (Sat, 06 Dec 2008) Log Message: ----------- Fixed a small bug in svg backend Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_svg.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-06 14:36:35 UTC (rev 6501) +++ trunk/matplotlib/CHANGELOG 2008-12-06 23:17:10 UTC (rev 6502) @@ -1,3 +1,6 @@ +2008-12-06 Fixed a bug in svg backend that new_figure_manager() + ignores keywords arguments such as figsize, etc. -JJL + 2008-12-05 Fixed a bug that the handlelength of the new legend class set too short when numpoints=1 -JJL Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-12-06 14:36:35 UTC (rev 6501) +++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2008-12-06 23:17:10 UTC (rev 6502) @@ -27,7 +27,7 @@ def new_figure_manager(num, *args, **kwargs): FigureClass = kwargs.pop('FigureClass', Figure) - thisFig = FigureClass(*args) + thisFig = FigureClass(*args, **kwargs) canvas = FigureCanvasSVG(thisFig) manager = FigureManagerSVG(canvas, num) return manager This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <lee...@us...> - 2008-12-07 20:40:17
|
Revision: 6503 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6503&view=rev Author: leejjoon Date: 2008-12-07 20:40:12 +0000 (Sun, 07 Dec 2008) Log Message: ----------- drop "new" keyword of np.histogram() for numpy 1.2 or later Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-12-06 23:17:10 UTC (rev 6502) +++ trunk/matplotlib/CHANGELOG 2008-12-07 20:40:12 UTC (rev 6503) @@ -1,3 +1,6 @@ +2008-12-07 drop the deprecated "new" keyword of np.histogram() for + numpy 1.2 or later. -JJL + 2008-12-06 Fixed a bug in svg backend that new_figure_manager() ignores keywords arguments such as figsize, etc. -JJL Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-12-06 23:17:10 UTC (rev 6502) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-12-07 20:40:12 UTC (rev 6503) @@ -6550,12 +6550,20 @@ # case do not autoscale axes. binsgiven = (cbook.iterable(bins) or range != None) + # check the version of the numpy + np_version = float(".".join(np.__version__.split(".")[:2])) + if np_version < 1.2: # version 1.1 + hist_kwargs = dict(range=range, + normed=bool(normed), new=True) + else: # version 1.2 and later, drop new=True + hist_kwargs = dict(range=range, + normed=bool(normed)) + n = [] for i in xrange(len(x)): # this will automatically overwrite bins, # so that each histogram uses the same bins - m, bins = np.histogram(x[i], bins, range=range, - normed=bool(normed), new=True) + m, bins = np.histogram(x[i], bins, **hist_kwargs) n.append(m) if cumulative: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-12-08 15:30:58
|
Revision: 6511 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6511&view=rev Author: jdh2358 Date: 2008-12-08 15:30:56 +0000 (Mon, 08 Dec 2008) Log Message: ----------- added rc param to control legend fancybox Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/legend_demo.py trunk/matplotlib/examples/pylab_examples/legend_demo3.py trunk/matplotlib/lib/matplotlib/legend.py trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/matplotlibrc.template Modified: trunk/matplotlib/examples/pylab_examples/legend_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/legend_demo.py 2008-12-08 15:10:20 UTC (rev 6510) +++ trunk/matplotlib/examples/pylab_examples/legend_demo.py 2008-12-08 15:30:56 UTC (rev 6511) @@ -15,7 +15,7 @@ ax = plt.subplot(111) plt.plot(a,c,'k--',a,d,'k:',a,c+d,'k') plt.legend(('Model length', 'Data length', 'Total message length'), - 'upper center', shadow=True) + 'upper center', shadow=True, fancybox=True) plt.ylim([-1,20]) plt.grid(False) plt.xlabel('Model complexity --->') Modified: trunk/matplotlib/examples/pylab_examples/legend_demo3.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2008-12-08 15:10:20 UTC (rev 6510) +++ trunk/matplotlib/examples/pylab_examples/legend_demo3.py 2008-12-08 15:30:56 UTC (rev 6511) @@ -1,5 +1,6 @@ #!/usr/bin/env python - +import matplotlib +matplotlib.rcParams['legend.fancybox'] = True import matplotlib.pyplot as plt import numpy as np import pylab @@ -22,7 +23,7 @@ ax3 = plt.subplot(3,1,3) myplot(ax3) -ax3.legend(loc=1, ncol=4, mode="expand", fancybox=False, shadow=True) +ax3.legend(loc=1, ncol=4, mode="expand", shadow=True) #title('Damped oscillation') Modified: trunk/matplotlib/lib/matplotlib/legend.py =================================================================== --- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-08 15:10:20 UTC (rev 6510) +++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-08 15:30:56 UTC (rev 6511) @@ -63,7 +63,7 @@ loc can be a tuple of the noramilzed coordinate values with respect its parent. - + Return value is a sequence of text, line instances that make up the legend """ @@ -94,15 +94,15 @@ scatterpoints = 3, # TODO: may be an rcParam scatteryoffsets=None, prop = None, # properties for the legend texts - + # the following dimensions are in axes coords pad = None, # deprecated; use borderpad - labelsep = None, # deprecated; use labelspacing - handlelen = None, # deprecated; use handlelength - handletextsep = None, # deprecated; use handletextpad + labelsep = None, # deprecated; use labelspacing + handlelen = None, # deprecated; use handlelength + handletextsep = None, # deprecated; use handletextpad axespad = None, # deprecated; use borderaxespad - # spacing & pad defined as a fractionof the font-size + # spacing & pad defined as a fractionof the font-size borderpad = None, # the whitespace inside the legend border labelspacing=None, #the vertical space between the legend entries handlelength=None, # the length of the legend handles @@ -113,7 +113,7 @@ ncol=1, # number of columns mode=None, # mode for horizontal distribution of columns. None, "expand" - fancybox=True, + fancybox=None, # True use a fancy box, false use a rounded box, none use rc shadow = None, ): """ @@ -131,7 +131,7 @@ numpoints the number of points in the legend line prop the font property markerscale the relative size of legend markers vs. original - fancybox if True, draw a frame with a round fancybox. + fancybox if True, draw a frame with a round fancybox. If None, use rc shadow if True, draw a shadow behind legend scatteryoffsets a list of yoffsets for scatter symbols in legend @@ -144,7 +144,7 @@ The dimensions of pad and spacing are given as a fraction of the fontsize. Values from rcParams will be used if None. - + """ from matplotlib.axes import Axes # local import only to avoid circularity from matplotlib.figure import Figure # local import only to avoid circularity @@ -172,7 +172,7 @@ # Take care the deprecated keywords deprecated_kwds = {"pad":"borderpad", "labelsep":"labelspacing", - "handlelen":"handlelength", + "handlelen":"handlelength", "handletextsep":"handletextpad", "axespad":"borderaxespad"} @@ -182,7 +182,7 @@ # conversion factor bbox = parent.bbox axessize_fontsize = min(bbox.width, bbox.height)/self.fontsize - + for k, v in deprecated_kwds.items(): # use deprecated value if not None and if their newer # counter part is None. @@ -199,7 +199,7 @@ setattr(self, v, localdict[v]) del localdict - + self._ncol = ncol if self.numpoints <= 0: @@ -265,6 +265,9 @@ # The width and height of the legendPatch will be set (in the # draw()) to the length that includes the padding. Thus we set # pad=0 here. + if fancybox is None: + fancybox = rcParams["legend.fancybox"] + if fancybox == True: self.legendPatch.set_boxstyle("round",pad=0, rounding_size=0.2) @@ -318,7 +321,7 @@ # find_offset function will be provided to _legend_box and # _legend_box will draw itself at the location of the return - # value of the find_offset. + # value of the find_offset. if self._loc == 0: self._legend_box.set_offset(self._findoffset_best) else: @@ -339,7 +342,7 @@ if self.shadow: shadow = Shadow(self.legendPatch, 2, -2) shadow.draw(renderer) - + self.legendPatch.draw(renderer) self._legend_box.draw(renderer) @@ -360,7 +363,7 @@ Initiallize the legend_box. The legend_box is an instance of the OffsetBox, which is packed with legend handles and texts. Once packed, their location is calculated during the - drawing time. + drawing time. """ # legend_box is a HPacker, horizontally packed with @@ -371,7 +374,7 @@ # is an instance of offsetbox.TextArea which contains legend # text. - + text_list = [] # the list of text instances handle_list = [] # the list of text instances @@ -394,12 +397,12 @@ # The approximate height and descent of text. These values are # only used for plotting the legend handle. height = self._approx_text_height() * 0.7 - descent = 0. + descent = 0. # each handle needs to be drawn inside a box of # (x, y, w, h) = (0, -descent, width, height). # And their corrdinates should be given in the display coordinates. - + # The transformation of each handle will be automatically set # to self.get_trasnform(). If the artist does not uses its # default trasnform (eg, Collections), you need to @@ -413,7 +416,7 @@ if npoints > 1: # we put some pad here to compensate the size of the # marker - xdata = np.linspace(0.3*self.fontsize, + xdata = np.linspace(0.3*self.fontsize, (self.handlelength-0.3)*self.fontsize, npoints) xdata_marker = xdata @@ -484,14 +487,14 @@ size_min] else: sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min - + p = type(handle)(handle.get_numsides(), rotation=handle.get_rotation(), sizes=sizes, offsets=zip(xdata_marker,ydata), transOffset=self.get_transform(), ) - + p.update_from(handle) p.set_figure(self.figure) p.set_clip_box(None) @@ -534,7 +537,7 @@ for h, t in handle_label[i0:i0+di]] # minimumdescent=False for the text of the last row of the column itemBoxes[-1].get_children()[1].set_minimumdescent(False) - + # pack columnBox columnbox.append(VPacker(pad=0, sep=self.labelspacing*self.fontsize, @@ -547,7 +550,7 @@ mode = "fixed" sep = self.columnspacing*self.fontsize - + self._legend_box = HPacker(pad=self.borderpad*self.fontsize, sep=sep, align="baseline", mode=mode, @@ -555,8 +558,8 @@ self.texts = text_list self.legendHandles = handle_list - + def _auto_legend_data(self): """ Returns list of vertices and extents covered by the plot. @@ -655,12 +658,12 @@ LC:"S", UC:"N", C:"C"} - + c = anchor_coefs[loc] container = parentbbox.padded(-(self.borderaxespad) * self.fontsize) anchored_box = bbox.anchored(c, container=container) - return anchored_box.x0, anchored_box.y0 + return anchored_box.x0, anchored_box.y0 def _find_best_position(self, width, height, consider=None): Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-08 15:10:20 UTC (rev 6510) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-12-08 15:30:56 UTC (rev 6511) @@ -417,6 +417,7 @@ 'polaraxes.grid' : [True, validate_bool], # display polar grid or not #legend properties + 'legend.fancybox' : [False,validate_bool], 'legend.loc' : ['upper right',validate_legend_loc], # at some point, this should be changed to 'best' 'legend.isaxes' : [True,validate_bool], # this option is internally ignored - it never served any useful purpose 'legend.numpoints' : [2, validate_int], # the number of points in the legend line Modified: trunk/matplotlib/matplotlibrc.template =================================================================== --- trunk/matplotlib/matplotlibrc.template 2008-12-08 15:10:20 UTC (rev 6510) +++ trunk/matplotlib/matplotlibrc.template 2008-12-08 15:30:56 UTC (rev 6511) @@ -235,6 +235,8 @@ #grid.linewidth : 0.5 # in points ### Legend +#legend.fancybox : False # if True, use a rounded box for the + # legend, else a rectangle #legend.isaxes : True #legend.numpoints : 2 # the number of points in the legend line #legend.fontsize : large This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |