You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(115) |
Aug
(120) |
Sep
(137) |
Oct
(170) |
Nov
(461) |
Dec
(263) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(120) |
Feb
(74) |
Mar
(35) |
Apr
(74) |
May
(245) |
Jun
(356) |
Jul
(240) |
Aug
(115) |
Sep
(78) |
Oct
(225) |
Nov
(98) |
Dec
(271) |
2009 |
Jan
(132) |
Feb
(84) |
Mar
(74) |
Apr
(56) |
May
(90) |
Jun
(79) |
Jul
(83) |
Aug
(296) |
Sep
(214) |
Oct
(76) |
Nov
(82) |
Dec
(66) |
2010 |
Jan
(46) |
Feb
(58) |
Mar
(51) |
Apr
(77) |
May
(58) |
Jun
(126) |
Jul
(128) |
Aug
(64) |
Sep
(50) |
Oct
(44) |
Nov
(48) |
Dec
(54) |
2011 |
Jan
(68) |
Feb
(52) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2018 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <js...@us...> - 2007-10-11 22:09:46
|
Revision: 3936 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3936&view=rev Author: jswhit Date: 2007-10-11 15:09:18 -0700 (Thu, 11 Oct 2007) Log Message: ----------- fix typo Modified Paths: -------------- trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py Modified: trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py =================================================================== --- trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py 2007-10-11 17:03:50 UTC (rev 3935) +++ trunk/toolkits/basemap/lib/matplotlib/toolkits/basemap/basemap.py 2007-10-11 22:09:18 UTC (rev 3936) @@ -2882,7 +2882,7 @@ lon0: starting longitude for shifted grid (ending longitude if start=False). lon0 must be on - input grid (with the range of lonsin). + input grid (within the range of lonsin). datain: original data. lonsin: original longitudes. start[True]: if True, lon0 represents the starting longitude This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-11 17:03:56
|
Revision: 3935 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3935&view=rev Author: mdboom Date: 2007-10-11 10:03:50 -0700 (Thu, 11 Oct 2007) Log Message: ----------- Fixed minor import bug Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/pyplot.py Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2007-10-11 15:42:53 UTC (rev 3934) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2007-10-11 17:03:50 UTC (rev 3935) @@ -2,7 +2,7 @@ import matplotlib from matplotlib import _pylab_helpers -from matplotlib.cbook import dedent, silent_list +from matplotlib.cbook import dedent, silent_list, is_string_like, is_numlike from matplotlib.figure import Figure, figaspect from matplotlib.backend_bases import FigureCanvasBase from matplotlib.image import imread as _imread @@ -1231,9 +1231,9 @@ def getname_val(identifier): 'return the name and column data for identifier' - if cbook.is_string_like(identifier): + if is_string_like(identifier): return identifier, r[identifier] - elif cbook.is_numlike(identifier): + elif is_numlike(identifier): name = r.dtype.names[int(identifier)] return name, r[name] else: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-11 15:42:55
|
Revision: 3934 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3934&view=rev Author: mdboom Date: 2007-10-11 08:42:53 -0700 (Thu, 11 Oct 2007) Log Message: ----------- Continued progress getting more examples to work. Working examples (with TkAgg backend only) are marked in PASSED_DEMOS for the curious. Modified Paths: -------------- branches/transforms/examples/histogram_demo_canvasagg.py branches/transforms/examples/line_collection2.py branches/transforms/examples/pick_event_demo.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/colorbar.py branches/transforms/lib/matplotlib/colors.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/image.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/mathtext.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/ticker.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Added Paths: ----------- branches/transforms/PASSED_DEMOS Added: branches/transforms/PASSED_DEMOS =================================================================== --- branches/transforms/PASSED_DEMOS (rev 0) +++ branches/transforms/PASSED_DEMOS 2007-10-11 15:42:53 UTC (rev 3934) @@ -0,0 +1,210 @@ +accented_text.py O +agg_buffer_to_array.py O +agg_oo.py O +agg_resize.py [BROKEN IN TRUNK] +agg_test.py +alignment_test.py O +animation_blit_fltk.py +animation_blit.py +animation_blit_qt4.py +animation_blit_qt.py +animation_blit_tk.py +animation_blit_wx.py +anim.py O [BUT SLOWER] +annotation_demo.py O +anscombe.py O +arctest.py O +arrow_demo.py O +axes_demo.py O +axes_props.py [SOMETHING FUNNY ABOUT DASHED LINES] +axhspan_demo.py O +axis_equal_demo.py O +backend_driver.py +barchart_demo.py O +barcode_demo.py O +barh_demo.py [BROKEN IN TRUNK] +bar_stacked.py O +boxplot_demo.py O +break.py O +broken_barh.py O +clippath_test.py O +clippedline.py O +collections_demo.py -- [NEEDS ADDITIONAL WORK] +colorbar_only.py O +color_by_yvalue.py O +color_demo.py O +colours.py [???] +contour_demo.py +contourf_demo.py +contour_image.py +coords_demo.py O +coords_report.py O +csd_demo.py O +cursor_demo.py O +custom_figure_class.py [EXCEPT FOR PS OUTPUT] +customize_rc.py +custom_ticker1.py O +dannys_example.py [REQUIRES NUMERIC] +dash_control.py O +dashpointlabel.py O +dashtick.py +data_browser.py O +data_helper.py [N/A] +date_demo1.py O +date_demo2.py O +date_demo_convert.py O [PASSES] +date_demo_rrule.py O [PASSES] +date_index_formatter.py O [PASSES] +dynamic_collection.py O +dynamic_demo.py [GTK] +dynamic_demo_wx.py [WX] +dynamic_image_gtkagg.py +dynamic_image_wxagg2.py +dynamic_image_wxagg.py +ellipse_demo.py O +ellipse_rotated.py O +embedding_in_gtk2.py +embedding_in_gtk3.py +embedding_in_gtk.py +embedding_in_qt4.py +embedding_in_qt.py +embedding_in_tk2.py +embedding_in_tk.py +embedding_in_wx2.py +embedding_in_wx3.py +embedding_in_wx4.py +embedding_in_wx.py +errorbar_demo.py O +errorbar_limits.py O +figimage_demo.py O +figlegend_demo.py [HORRIBLY BROKEN] +figtext.py O +fill_between_posneg.py O +fill_between.py O +fill_demo2.py O +fill_demo.py O +fill_spiral.py O +finance_demo.py O +font_indexing.py O +fonts_demo_kw.py O +fonts_demo.py O +font_table_ttf.py [N/A] +ftface_props.py [N/A] +ganged_plots.py O +glyph_to_path.py [Requires PIL] +gradient_bar.py O +gtk_spreadsheet.py +hatch_demo.py [Requires PS] +histogram_demo_canvasagg.py [???] +histogram_demo.py O +image_demo2.py O +image_demo3.py [Requires PIL] +image_demo.py O +image_interp.py O +image_masked.py O [Whew!] +image_origin.py O +image_slices_viewer.py [BROKEN ON TRUNK] +__init__.py +integral_demo.py O +interactive2.py [N/A] +interactive.py [N/A] +interp_demo.py O +invert_axes.py O +keypress_demo.py [BROKEN IN TRUNK] +lasso_demo.py O +layer_images.py O +legend_auto.py [WEIRD z-order problem in figure 10] +legend_demo2.py O +legend_demo.py O +legend_scatter.py O +line_collection2.py O +line_collection.py O +lineprops_dialog_gtk.py +line_styles.py O +load_converter.py O +loadrec.py O +log_bar.py O +log_demo.py O +logo.py O +log_test.py O +major_minor_demo1.py O +major_minor_demo2.py O +masked_demo.py O +mathtext_demo.py O +mathtext_examples.py O +mathtext_wx.py +matplotlib_icon.py [N/A] +matshow.py O +movie_demo.py O +mpl_with_glade.py [N/A] +mri_demo.py O +mri_with_eeg.py +multi_image.py O +multiline.py O +multiple_figs_demo.py O +newscalarformatter_demo.py O +pcolor_demo2.py O +pcolor_demo.py O +pcolor_log.py O +pcolor_nonuniform.py O +pcolor_small.py O +pick_event_demo2.py +pick_event_demo.py +pie_demo.py +plotfile_demo.py +polar_bar.py +polar_demo.py +polar_legend.py +polar_scatter.py +poly_editor.py +poormans_contour.py +printing_in_wx.py +print_stdout.py +psd_demo.py +pstest.py +pylab_with_gtk.py +pythonic_matplotlib.py +quadmesh_demo.py +quiver_demo.py +rc_traits.py +scatter_custom_symbol.py +scatter_demo2.py +scatter_demo.py +scatter_masked.py +scatter_profile.py +scatter_star_poly.py +set_and_get.py +shared_axis_across_figures.py +shared_axis_demo.py +simple3d_oo.py +simple3d.py +simple_plot_fps.py +simple_plot.py +specgram_demo.py +spy_demos.py +stem_plot.py +step_demo.py +stock_demo.py +strip_chart_demo.py +subplot_demo.py +subplots_adjust.py +subplot_toolbar.py +system_monitor.py +table_demo.py +tex_demo.py +text_handles.py +text_rotation.py +text_themes.py +tex_unicode_demo.py +toggle_images.py +to_numeric.py +transoffset.py +two_scales.py +unicode_demo.py +vertical_ticklabels.py +vline_demo.py +webapp_demo.py +wxcursor_demo.py +xcorr_demo.py +zoom_window.py +zorder_demo.py Modified: branches/transforms/examples/histogram_demo_canvasagg.py =================================================================== --- branches/transforms/examples/histogram_demo_canvasagg.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/examples/histogram_demo_canvasagg.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -11,7 +11,6 @@ """ from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure -from matplotlib.axes import Subplot from matplotlib.mlab import normpdf from numpy.random import randn import numpy @@ -45,7 +44,7 @@ # get the figure dimensions for creating bitmaps or numpy arrays, # etc. -l,b,w,h = fig.bbox.get_bounds() +l,b,w,h = fig.bbox.bounds w, h = int(w), int(h) if 0: Modified: branches/transforms/examples/line_collection2.py =================================================================== --- branches/transforms/examples/line_collection2.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/examples/line_collection2.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -23,7 +23,7 @@ # See matplotlib.collections.LineCollection for more information line_segments = LineCollection([zip(x,y) for y in ys], # Make a sequence of x,y pairs linewidths = (0.5,1,1.5,2), - linestyle = 'solid') + linestyles = 'solid') line_segments.set_array(x) ax.add_collection(line_segments) fig = gcf() Modified: branches/transforms/examples/pick_event_demo.py =================================================================== --- branches/transforms/examples/pick_event_demo.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/examples/pick_event_demo.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -93,7 +93,7 @@ print 'onpick1 line:', zip(nx.take(xdata, ind), nx.take(ydata, ind)) elif isinstance(event.artist, Rectangle): patch = event.artist - print 'onpick1 patch:', patch.get_verts() + print 'onpick1 patch:', patch.get_path() elif isinstance(event.artist, Text): text = event.artist print 'onpick1 text:', text.get_text() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -480,8 +480,7 @@ if isinstance(rect, mtransforms.Bbox): self._position = rect else: - warnings.warn("Passing non-bbox as rect to Axes") - mtransforms.Bbox.from_lbwh(*rect) + self._position = mtransforms.Bbox.from_lbwh(*rect) self._originalPosition = self._position.frozen() self.set_axes(self) self.set_aspect('auto') @@ -596,7 +595,7 @@ def get_xaxis_text2_transform(self, pad_pixels): return (self._xaxis_transform + mtransforms.Affine2D().translate(0, pad_pixels), - "top", "center") + "bottom", "center") def get_yaxis_transform(self): return self._yaxis_transform @@ -609,7 +608,7 @@ def get_yaxis_text2_transform(self, pad_pixels): return (self._yaxis_transform + mtransforms.Affine2D().translate(pad_pixels, 0), - "center", "right") + "center", "left") def _update_transScale(self): self.transScale.set( @@ -643,8 +642,8 @@ self._position.set(pos) if which in ('both', 'original'): self._originalPosition.set(pos) + - def _set_artist_props(self, a): 'set the boilerplate props for artists added to axes' a.set_figure(self.figure) @@ -1083,7 +1082,8 @@ def _update_patch_limits(self, p): 'update the datalimits for patch p' xys = self._get_verts_in_data_coords( - p.get_transform(), p.get_path().vertices) + p.get_data_transform(), + p.get_patch_transform().transform(p.get_path().vertices)) self.update_datalim(xys) @@ -1240,11 +1240,11 @@ for im in self.images if im.get_visible()] - im = mimage.from_images(self.bbox.height()*mag, - self.bbox.width()*mag, + im = mimage.from_images(self.bbox.height*mag, + self.bbox.width*mag, ims) im.is_grayscale = False - l, b, w, h = self.bbox.get_bounds() + l, b, w, h = self.bbox.bounds # composite images need special args so they will not # respect z-order for now renderer.draw_image(l, b, im, self.bbox) Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -1290,14 +1290,13 @@ else: if not len(bboxes2): - right = self.axes.bbox.xmax() + right = self.axes.bbox.xmax else: + bbox = Bbox.union(bboxes2) + right = bbox.xmax - bbox = bbox_union(bboxes2) - right = bbox.xmax() + self.label.set_position( (right+self.LABELPAD*self.figure.dpi/72.0, y)) - self.label.set_position( (right+self.LABELPAD*self.figure.dpi.get()/72.0, y)) - def _update_offset_text_position(self, bboxes, bboxes2): """ Update the offset_text position based on the sequence of bounding Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -23,6 +23,9 @@ from matplotlib.backends._backend_agg import get_path_collection_extents, \ point_in_path_collection +# MGDTODO: Treat facecolors and edgecolors as numpy arrays always +# and then update draw_path_collection to use the array interface + class Collection(artist.Artist, cm.ScalarMappable): """ Base class for Collections. Must be subclassed to be usable. @@ -58,7 +61,6 @@ """ _offsets = npy.zeros((1, 2)) _transOffset = transforms.IdentityTransform() - _transforms = [None] zorder = 1 @@ -163,9 +165,9 @@ self.update_scalarmappable() - #print 'calling renderer draw line collection' clippath, clippath_trans = self.get_transformed_clip_path_and_affine() + # MGDTODO: This may benefit from using TransformedPath if not transform.is_affine: paths = [transform.transform_path_non_affine(path) for path in paths] transform = transform.get_affine() @@ -185,10 +187,19 @@ Returns T/F, dict(ind=itemlist), where every item in itemlist contains the event. """ if callable(self._contains): return self._contains(self,mouseevent) + + transform = self.get_transform() + paths = self.get_paths() + if not transform.is_affine: + paths = [transform.transform_path_non_affine(path) for path in paths] + transform = transform.get_affine() + + # MGDTODO: Don't pick when outside of clip path / clip box ind = point_in_path_collection( mouseevent.x, mouseevent.y, self._pickradius, - self.get_transform(), self._paths, self._transforms, self._offsets, - self._offsetTrans, self._facecolors) + transform.frozen(), paths, self.get_transforms(), + npy.asarray(self._offsets, npy.float_), + self._transOffset.frozen(), len(self._facecolors)) return len(ind)>0,dict(ind=ind) # MGDTODO: Update @@ -233,7 +244,7 @@ def set_pickradius(self,pickradius): self.pickradius = 5 def get_pickradius(self): return self.pickradius - def set_linewidth(self, lw): + def set_linewidths(self, lw): """ Set the linewidth(s) for the collection. lw can be a scalar or a sequence; if it is a sequence the patches will cycle through the @@ -242,9 +253,8 @@ ACCEPTS: float or sequence of floats """ self._linewidths = self._get_value(lw) - def set_linewidths(self, lw): - self.set_linewidth(lw) - + set_linewidth = set_linewidths + def set_linestyles(self, ls): """ Set the linestyles(s) for the collection. @@ -274,6 +284,7 @@ raise ValueError('Do not know how to convert %s to dashes'%ls) self._linestyles = dashes + set_dashes = set_linestyle = set_linestyles def set_color(self, c): """ @@ -295,8 +306,7 @@ ACCEPTS: matplotlib color arg or sequence of rgba tuples """ self._facecolors = _colors.colorConverter.to_rgba_list(c) - def set_facecolors(self, c): - self.set_facecolor(c) + set_facecolors = set_facecolor def set_edgecolor(self, c): """ @@ -308,10 +318,10 @@ """ if c == 'None': self._linewidths = (0.0,) + self._edgecolors = npy.array([]) else: self._edgecolors = _colors.colorConverter.to_rgba_list(c) - def set_edgecolors(self, c): - self.set_edgecolor(c) + set_edgecolors = set_edgecolor def set_alpha(self, alpha): """ @@ -324,29 +334,30 @@ except TypeError: raise TypeError('alpha must be a float') else: artist.Artist.set_alpha(self, alpha) - self._facecolors = [(r,g,b,alpha) for r,g,b,a in self._facecolors] - if cbook.is_string_like(self._edgecolors) and self._edgecolors != 'None': - self._edgecolors = [(r,g,b,alpha) for r,g,b,a in self._edgecolors] + self._facecolors[:, 3] = alpha + self._edgecolors[:, 3] = alpha - def get_linewidth(self): + def get_linewidths(self): return self._linewidths + get_linewidth = get_linewidths - def get_linestyle(self): + def get_linestyles(self): return self._linestyles - - def get_dashes(self): - return self._linestyles + get_dashes = get_linestyle = get_linestyles def update_scalarmappable(self): """ - If the scalar mappable array is not none, update facecolors + If the scalar mappable array is not none, update colors from scalar data """ #print 'update_scalarmappable: self._A', self._A if self._A is None: return if len(self._A.shape)>1: raise ValueError('PatchCollections can only map rank 1 arrays') - self._facecolors = self.to_rgba(self._A, self._alpha) + if len(self._facecolors): + self._facecolors = self.to_rgba(self._A, self._alpha) + else: + self._edgecolors = self.to_rgba(self._A, self._alpha) #print self._facecolors @@ -673,6 +684,8 @@ if antialiaseds is None: antialiaseds = (mpl.rcParams['lines.antialiased'],) self.set_linestyles(linestyles) + colors = _colors.colorConverter.to_rgba_list(colors) + Collection.__init__( self, edgecolors=colors, @@ -686,7 +699,7 @@ pickradius=pickradius, **kwargs) - self._facecolors = [None] + self._facecolors = npy.array([]) self.set_segments(segments) def get_paths(self): Modified: branches/transforms/lib/matplotlib/colorbar.py =================================================================== --- branches/transforms/lib/matplotlib/colorbar.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/colorbar.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -319,9 +319,9 @@ else: intv = self.vmin, self.vmax locator.create_dummy_axis() + formatter.create_dummy_axis() locator.set_view_interval(*intv) locator.set_data_interval(*intv) - formatter.create_dummy_axis() formatter.set_view_interval(*intv) formatter.set_data_interval(*intv) b = npy.array(locator()) @@ -580,18 +580,18 @@ shrink = kw.pop('shrink', 1.0) aspect = kw.pop('aspect', 20) #pb = transforms.PBox(parent.get_position()) - pb = transforms.PBox(parent.get_position(original=True)) + pb = parent.get_position(original=True).frozen() if orientation == 'vertical': pad = kw.pop('pad', 0.05) x1 = 1.0-fraction pb1, pbx, pbcb = pb.splitx(x1-pad, x1) - pbcb.shrink(1.0, shrink).anchor('C') + pbcb = pbcb.shrunk(1.0, shrink).anchored('C', pbcb) anchor = (0.0, 0.5) panchor = (1.0, 0.5) else: pad = kw.pop('pad', 0.15) pbcb, pbx, pb1 = pb.splity(fraction, fraction+pad) - pbcb.shrink(shrink, 1.0).anchor('C') + pbcb = pbcb.shrunk(shrink, 1.0).anchored('C', pbcb) aspect = 1.0/aspect anchor = (0.5, 1.0) panchor = (0.5, 0.0) Modified: branches/transforms/lib/matplotlib/colors.py =================================================================== --- branches/transforms/lib/matplotlib/colors.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/colors.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -316,7 +316,7 @@ If the sequence is a list, the list items are changed in place. """ try: - return [self.to_rgba(c, alpha)] + result = [self.to_rgba(c, alpha)] except ValueError: # If c is a list it must be maintained as the same list # with modified items so that items can be appended to @@ -325,7 +325,8 @@ c = list(c) for i, cc in enumerate(c): c[i] = self.to_rgba(cc, alpha) # change in place - return c + result = c + return npy.asarray(result, npy.float_) colorConverter = ColorConverter() Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/figure.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -623,11 +623,11 @@ mag = renderer.get_image_magnification() ims = [(im.make_image(mag), im.ox*mag, im.oy*mag) for im in self.images] - im = _image.from_images(self.bbox.height()*mag, - self.bbox.width()*mag, + im = _image.from_images(self.bbox.height * mag, + self.bbox.width * mag, ims) im.is_grayscale = False - l, b, w, h = self.bbox.get_bounds() + l, b, w, h = self.bbox.bounds renderer.draw_image(l, b, im, self.bbox) Modified: branches/transforms/lib/matplotlib/image.py =================================================================== --- branches/transforms/lib/matplotlib/image.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/image.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -332,8 +332,8 @@ if self._A is None: raise RuntimeError('You must first set the image array') - x0, y0, v_width, v_height = self.axes.viewLim.get_bounds() - l, b, width, height = self.axes.bbox.get_bounds() + x0, y0, v_width, v_height = self.axes.viewLim.bounds + l, b, width, height = self.axes.bbox.bounds width *= magnification height *= magnification im = _image.pcolor(self._Ax, self._Ay, self._A, Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/legend.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -38,7 +38,7 @@ def line_cuts_bbox(line, bbox): """ Return True if and only if line cuts bbox. """ - minx, miny, width, height = bbox.get_bounds() + minx, miny, width, height = bbox.bounds maxx = minx + width maxy = miny + height @@ -343,31 +343,27 @@ bboxes = [] lines = [] - inv = ax.transAxes.inverse_xy_tup + inv = ax.transAxes.inverted().transform for handle in ax.lines: assert isinstance(handle, Line2D) - - xdata = handle.get_xdata(orig=False) - ydata = handle.get_ydata(orig=False) + data = handle.get_xydata() trans = handle.get_transform() - xt, yt = trans.numerix_x_y(xdata, ydata) - - # XXX need a special method in transform to do a list of verts - averts = [inv(v) for v in zip(xt, yt)] + tdata = trans.transform(data) + averts = inv(tdata) lines.append(averts) for handle in ax.patches: assert isinstance(handle, Patch) - verts = handle.get_verts() + path = handle.get_path() trans = handle.get_transform() - tverts = trans.seq_xy_tups(verts) + tpath = trans.transform_path(path) + tverts = tpath.vertices + averts = inv(tverts) - averts = [inv(v) for v in tverts] - - bbox = unit_bbox() - bbox.update(averts, True) + bbox = Bbox.unit() + bbox.update_from_data_xy(averts, True) bboxes.append(bbox) for handle in ax.collections: @@ -445,22 +441,19 @@ consider = [self._loc_to_axes_coords(x, width, height) for x in range(1, len(self.codes))] - tx, ty = self.legendPatch.xy + tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y() candidates = [] for l, b in consider: legendBox = Bbox.from_lbwh(l, b, width, height) badness = 0 badness = legendBox.count_contains(verts) - ox, oy = l-tx, b-ty - for bbox in bboxes: - if legendBox.overlaps(bbox): - badness += 1 - + badness += legendBox.count_overlaps(bboxes) for line in lines: if line_cuts_bbox(line, legendBox): badness += 1 + ox, oy = l-tx, b-ty if badness == 0: return ox, oy @@ -564,6 +557,4 @@ self._offset(ox, oy) - - #artist.kwdocd['Legend'] = kwdoc(Legend) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -350,16 +350,15 @@ self._picker = p def get_window_extent(self, renderer): - bbox = Bbox() - bbox.update_from_data(self.get_transform().transform(self._xy)) - + bbox = Bbox.unit() + bbox.update_from_data_xy(self.get_transform().transform(self._xy), + ignore=True) # correct for marker size, if any if self._marker is not None: - ms = self._markersize / 72.0 * self.figure.dpi - bbox = Bbox(bbox.get_points() + [[-ms/2.0, ms/2.0]]) + ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5 + bbox = Bbox(bbox.get_points() + [[-ms, -ms], [ms, ms]]) return bbox - def set_axes(self, ax): Artist.set_axes(self, ax) if ax.xaxis is not None: Modified: branches/transforms/lib/matplotlib/mathtext.py =================================================================== --- branches/transforms/lib/matplotlib/mathtext.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/mathtext.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -672,7 +672,8 @@ _slanted_symbols = Set(r"\int \oint".split()) def _get_glyph(self, fontname, sym, fontsize): - if fontname in self.fontmap and latex_to_bakoma.has_key(sym): + use_default = fontname == 'default' and len(sym) == 1 + if latex_to_bakoma.has_key(sym) and not use_default: basename, num = latex_to_bakoma[sym] slanted = (basename == "cmmi10") or sym in self._slanted_symbols cached_font = self._get_font(basename) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/patches.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -114,6 +114,9 @@ self._combined_transform = self.get_patch_transform() + \ artist.Artist.get_transform(self) + def get_data_transform(self): + return artist.Artist.get_transform(self) + def get_patch_transform(self): return transforms.IdentityTransform() @@ -307,8 +310,8 @@ g = rho*g b = rho*b - self.set_facecolor((r,g,b)) - self.set_edgecolor((r,g,b)) + self.set_facecolor((r,g,b,0.5)) + self.set_edgecolor((r,g,b,0.5)) def get_path(self): return self.patch.get_path() @@ -960,7 +963,7 @@ pad = props.pop('pad', 4) pad = renderer.points_to_pixels(pad) bbox = artist.get_window_extent(renderer) - l,b,w,h = bbox.get_bounds() + l,b,w,h = bbox.bounds l-=pad/2. b-=pad/2. w+=pad Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/ticker.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -118,8 +118,8 @@ axis = None class DummyAxis: def __init__(self): - self.dataLim = mtransforms.Bbox - self.viewLim = mtransforms.Bbox + self.dataLim = mtransforms.Bbox.unit() + self.viewLim = mtransforms.Bbox.unit() def get_view_interval(self): return self.viewLim.intervalx @@ -137,7 +137,8 @@ self.axis = axis def create_dummy_axis(self): - self.axis = self.DummyAxis() + if self.axis is None: + self.axis = self.DummyAxis() def set_view_interval(self, vmin, vmax): self.axis.set_view_interval(vmin, vmax) @@ -229,8 +230,8 @@ def __call__(self, x, pos=None): 'Return the format for tick val x at position pos' - self.verify_intervals() - d = abs(self.viewInterval.span()) + xmin, xmax = self.axis.get_view_interval() + d = abs(xmax - xmin) return self.pprint_val(x,d) @@ -322,7 +323,7 @@ if self.offset > 0: offsetStr = '+' + offsetStr if self.orderOfMagnitude: if self._usetex or self._useMathText: - sciNotStr = r'{\times}'+self.format_data(10**self.orderOfMagnitude) + sciNotStr = r'\times'+self.format_data(10**self.orderOfMagnitude) else: sciNotStr = u'\xd7'+'1e%d'% self.orderOfMagnitude if self._useMathText: @@ -451,8 +452,8 @@ def __call__(self, x, pos=None): 'Return the format for tick val x at position pos' - self.verify_intervals() - d = abs(self.viewInterval.span()) + vmin, vmax = self.axis.get_view_interval() + d = abs(vmax - vmin) b=self._base # only label the decades fx = math.log(x)/math.log(b) @@ -769,10 +770,7 @@ def __call__(self): 'Return the locations of the ticks' - - self.verify_intervals() - - vmin, vmax = self.viewInterval.get_bounds() + vmin, vmax = self.axis.get_view_interval() if vmax<vmin: vmin, vmax = vmax, vmin vmin = self._base.ge(vmin) @@ -786,10 +784,8 @@ Set the view limits to the nearest multiples of base that contain the data """ + dmin, dmax = self.axis.get_data_interval() - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - vmin = self._base.le(dmin) vmax = self._base.ge(dmax) if vmin==vmax: Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-11 15:42:53 UTC (rev 3934) @@ -268,20 +268,24 @@ def contains(self, x, y): return self.containsx(x) and self.containsy(y) - def overlapsx(self, other): - xmin, xmax = other.intervalx - return self.containsx(xmin) \ - or self.containsx(xmax) + def overlaps(self, other): + ax1, ay1, ax2, ay2 = self._get_lbrt() + bx1, by1, bx2, by2 = other._get_lbrt() - def overlapsy(self, other): - ymin, ymax = other.intervaly - return self.containsy(ymin) \ - or self.containsy(ymax) + if ax2 < ax1: + ax2, ax1 = ax1, ax2 + if ay2 < ay1: + ay2, ay1 = ay1, ay2 + if bx2 < bx1: + bx2, bx1 = bx1, bx2 + if by2 < by1: + by2, by1 = by1, by2 + + return not ((bx2 < ax1) or + (by2 < ay1) or + (bx1 > ax2) or + (by1 > ay2)) - def overlaps(self, other): - return self.overlapsx(other) \ - and self.overlapsy(other) - def fully_containsx(self, x): xmin, xmax = self.intervalx return ((xmin < xmax @@ -298,20 +302,24 @@ return self.fully_containsx(x) \ and self.fully_containsy(y) - def fully_overlapsx(self, other): - xmin, xmax = other.intervalx - return self.fully_containsx(xmin) \ - or self.fully_containsx(xmax) - - def fully_overlapsy(self, other): - ymin, ymax = other.intervaly - return self.fully_containsy(ymin) \ - or self.fully_containsy(ymax) - def fully_overlaps(self, other): - return self.fully_overlapsx(other) and \ - self.fully_overlapsy(other) + ax1, ay1, ax2, ay2 = self._get_lbrt() + bx1, by1, bx2, by2 = other._get_lbrt() + if ax2 < ax1: + ax2, ax1 = ax1, ax2 + if ay2 < ay1: + ay2, ay1 = ay1, ay2 + if bx2 < bx1: + bx2, bx1 = bx1, bx2 + if by2 < by1: + by2, by1 = by1, by2 + + return not ((bx2 <= ax1) or + (by2 <= ay1) or + (bx1 >= ax2) or + (by1 >= ay2)) + def transformed(self, transform): """ Return a new Bbox object, transformed by the given transform. @@ -324,8 +332,139 @@ given transform. """ return Bbox(transform.inverted().transform(self.get_points())) - - + + coefs = {'C': (0.5, 0.5), + 'SW': (0,0), + 'S': (0.5, 0), + 'SE': (1.0, 0), + 'E': (1.0, 0.5), + 'NE': (1.0, 1.0), + 'N': (0.5, 1.0), + 'NW': (0, 1.0), + 'W': (0, 0.5)} + def anchored(self, c, container = None): + """ + Return a copy of the Bbox, shifted to position c within a + container. + + c: may be either a) a sequence (cx, cy) where cx, cy range + from 0 to 1, where 0 is left or bottom and 1 is right or top; + or b) a string: C for centered, S for bottom-center, SE for + bottom-left, E for left, etc. + + Optional arg container is the lbwh box within which the BBox + is positioned; it defaults to the initial BBox. + """ + if container is None: + container = self + l, b, w, h = container.bounds + if isinstance(c, str): + cx, cy = self.coefs[c] + else: + cx, cy = c + L, B, W, H = self.bounds + return Bbox(self._points + + [(l + cx * (w-W)) - L, + (b + cy * (h-H)) - B]) + + def shrunk(self, mx, my): + """ + Return a copy of the Bbox, shurnk by the factor mx in the x + direction and the factor my in the y direction. The lower + left corner of the box remains unchanged. Normally mx and my + will be <= 1, but this is not enforced. + """ + w, h = self.size + return Bbox([self._points[0], + self._points[0] + [mx * w, my * h]]) + + def shrunk_to_aspect(self, box_aspect, container = None, fig_aspect = 1.0): + """ + Return a copy of the Bbox, shrunk so that it is as large as it + can be while having the desired aspect ratio, box_aspect. If + the box coordinates are relative--that is, fractions of a + larger box such as a figure--then the physical aspect ratio of + that figure is specified with fig_aspect, so that box_aspect + can also be given as a ratio of the absolute dimensions, not + the relative dimensions. + """ + assert box_aspect > 0 and fig_aspect > 0 + if container is None: + container = self + w, h = container.size + H = w * box_aspect/fig_aspect + if H <= h: + W = w + else: + W = h * fig_aspect/box_aspect + H = h + return Bbox([self._points[0], + self._points[0] + (W, H)]) + + def splitx(self, *args): + ''' + e.g., bbox.splitx(f1, f2, ...) + + Returns a list of new BBoxes formed by + splitting the original one with vertical lines + at fractional positions f1, f2, ... + ''' + boxes = [] + xf = [0] + list(args) + [1] + l, b, r, t = self.lbrt + w = r - l + for xf0, xf1 in zip(xf[:-1], xf[1:]): + boxes.append(Bbox([[l + xf0 * w, b], [l + xf1 * w, t]])) + return boxes + + def splity(self, *args): + ''' + e.g., bbox.splitx(f1, f2, ...) + + Returns a list of new PBoxes formed by + splitting the original one with horizontal lines + at fractional positions f1, f2, ... + ''' + boxes = [] + yf = [0] + list(args) + [1] + l, b, r, t = self.lbrt + h = t - b + for yf0, yf1 in zip(yf[:-1], yf[1:]): + boxes.append(Bbox([[l, b + yf0 * h], [r, b + yf1 * h]])) + return boxes + + def count_contains(self, vertices): + if len(vertices) == 0: + return 0 + vertices = npy.asarray(vertices) + xmin, ymin, xmax, ymax = self._get_lbrt() + dxmin = npy.sign(vertices[:, 0] - xmin) + dymin = npy.sign(vertices[:, 1] - ymin) + dxmax = npy.sign(vertices[:, 0] - xmax) + dymax = npy.sign(vertices[:, 1] - ymax) + inside = (abs(dxmin + dxmax) + abs(dymin + dymax)) <= 2 + return N.sum(inside) + + def count_overlaps(self, bboxes): + ax1, ay1, ax2, ay2 = self._get_lbrt() + if ax2 < ax1: + ax2, ax1 = ax1, ax2 + if ay2 < ay1: + ay2, ay1 = ay1, ay2 + + count = 0 + for bbox in bboxes: + bx1, by1, bx2, by2 = bbox._get_lbrt() + if bx2 < bx1: + bx2, bx1 = bx1, bx2 + if by2 < by1: + by2, by1 = by1, by2 + count += (not ((bx2 <= ax1) or + (by2 <= ay1) or + (bx1 >= ax2) or + (by1 >= ay2))) + return count + class Bbox(BboxBase): def __init__(self, points): """ @@ -402,7 +541,7 @@ if len(x) == 0 or len(y) == 0: return - + if ma.isMaskedArray(x) or ma.isMaskedArray(y): xpos = ma.where(x > 0.0, x, npy.inf) ypos = ma.where(y > 0.0, y, npy.inf) @@ -430,6 +569,7 @@ max(y.max(), self.ymax)]], npy.float_) self._minpos = npy.minimum(minpos, self._minpos) + self.invalidate() def update_from_data_xy(self, xy, ignore=None): @@ -544,74 +684,6 @@ """ return Bbox(self._points + (tx, ty)) - coefs = {'C': (0.5, 0.5), - 'SW': (0,0), - 'S': (0.5, 0), - 'SE': (1.0, 0), - 'E': (1.0, 0.5), - 'NE': (1.0, 1.0), - 'N': (0.5, 1.0), - 'NW': (0, 1.0), - 'W': (0, 0.5)} - def anchored(self, c, container = None): - """ - Return a copy of the Bbox, shifted to position c within a - container. - - c: may be either a) a sequence (cx, cy) where cx, cy range - from 0 to 1, where 0 is left or bottom and 1 is right or top; - or b) a string: C for centered, S for bottom-center, SE for - bottom-left, E for left, etc. - - Optional arg container is the lbwh box within which the BBox - is positioned; it defaults to the initial BBox. - """ - if container is None: - container = self - l, b, w, h = container.bounds - if isinstance(c, str): - cx, cy = self.coefs[c] - else: - cx, cy = c - L, B, W, H = self.bounds - return Bbox(self._points + - [(l + cx * (w-W)) - L, - (b + cy * (h-H)) - B]) - - def shrunk(self, mx, my): - """ - Return a copy of the Bbox, shurnk by the factor mx in the x - direction and the factor my in the y direction. The lower - left corner of the box remains unchanged. Normally mx and my - will be <= 1, but this is not enforced. - """ - w, h = self.size - return Bbox([self._points[0], - self._points[1] - [w - (mx * w), h - (my * h)]]) - - def shrunk_to_aspect(self, box_aspect, container = None, fig_aspect = 1.0): - """ - Return a copy of the Bbox, shrunk so that it is as large as it - can be while having the desired aspect ratio, box_aspect. If - the box coordinates are relative--that is, fractions of a - larger box such as a figure--then the physical aspect ratio of - that figure is specified with fig_aspect, so that box_aspect - can also be given as a ratio of the absolute dimensions, not - the relative dimensions. - """ - assert box_aspect > 0 and fig_aspect > 0 - if container is None: - container = self - w, h = container.size - H = w * box_aspect/fig_aspect - if H <= h: - W = w - else: - W = h * fig_aspect/box_aspect - H = h - return Bbox([self._points[0], - self._points[0] + (W, H)]) - #@staticmethod def union(bboxes): """ @@ -1444,6 +1516,7 @@ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 is_separable = property(_get_is_separable) + class IdentityTransform(Affine2DBase): """ A special class that does on thing, the identity transform, in a @@ -1478,10 +1551,10 @@ transform_path.__doc__ = Affine2DBase.transform_path.__doc__ transform_path_affine = transform_path - transform_path_affine = Affine2DBase.transform_path_affine.__doc__ + transform_path_affine.__doc__ = Affine2DBase.transform_path_affine.__doc__ transform_path_non_affine = transform_path - transform_path_non_affine = Affine2DBase.transform_path_non_affine.__doc__ + transform_path_non_affine.__doc__ = Affine2DBase.transform_path_non_affine.__doc__ def get_affine(self): return self Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/src/_backend_agg.cpp 2007-10-11 15:42:53 UTC (rev 3934) @@ -605,8 +605,6 @@ agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { - x += 0.5; - y += 0.5; //render the fill if (face.first) { if (has_clippath) { @@ -948,8 +946,8 @@ Py::SeqBase<Py::Object> transforms_obj = args[5]; Py::Object offsets_obj = args[6]; agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7], false); - Py::SeqBase<Py::Object> facecolors_obj = args[8]; - Py::SeqBase<Py::Object> edgecolors_obj = args[9]; + Py::Object facecolors_obj = args[8]; + Py::Object edgecolors_obj = args[9]; Py::SeqBase<Py::Float> linewidths = args[10]; Py::SeqBase<Py::Object> linestyles_obj = args[11]; Py::SeqBase<Py::Int> antialiaseds = args[12]; @@ -960,16 +958,31 @@ if (!offsets || offsets->dimensions[1] != 2) throw Py::ValueError("Offsets array must be Nx2"); + PyArrayObject* facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); + if (!facecolors || + (facecolors->nd == 1 && facecolors->dimensions[0] != 0) || + (facecolors->nd == 2 && facecolors->dimensions[1] != 4)) + throw Py::ValueError("Facecolors must be a Nx4 numpy array or empty"); + + PyArrayObject* edgecolors = (PyArrayObject*)PyArray_FromObject(edgecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); + if (!edgecolors || + (edgecolors->nd == 1 && edgecolors->dimensions[0] != 0) || + (edgecolors->nd == 2 && edgecolors->dimensions[1] != 4)) + throw Py::ValueError("Edgecolors must be a Nx4 numpy array"); + size_t Npaths = paths.length(); size_t Noffsets = offsets->dimensions[0]; size_t N = std::max(Npaths, Noffsets); size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t Nfacecolors = std::min(facecolors_obj.length(), N); - size_t Nedgecolors = std::min(edgecolors_obj.length(), N); + size_t Nfacecolors = facecolors->dimensions[0]; + size_t Nedgecolors = edgecolors->dimensions[0]; size_t Nlinewidths = linewidths.length(); size_t Nlinestyles = std::min(linestyles_obj.length(), N); size_t Naa = antialiaseds.length(); + if ((Nfacecolors == 0 && Nedgecolors == 0) || N == 0) + return Py::Object(); + size_t i = 0; // Convert all of the transforms up front @@ -983,46 +996,6 @@ transforms.push_back(trans); } - // Convert all of the facecolors up front - typedef std::vector<facepair_t> facecolors_t; - facecolors_t facecolors; - facecolors.resize(Nfacecolors); - i = 0; - for (facecolors_t::iterator f = facecolors.begin(); - f != facecolors.end(); ++f, ++i) { - double r, g, b, a; - const Py::Object& facecolor_obj = facecolors_obj[i]; - if (facecolor_obj.ptr() == Py_None) - f->first = false; - else { - Py::SeqBase<Py::Float> facergba = facecolor_obj; - r = Py::Float(facergba[0]); - g = Py::Float(facergba[1]); - b = Py::Float(facergba[2]); - a = 1.0; - if (facergba.size() == 4) - a = Py::Float(facergba[3]); - f->first = true; - f->second = agg::rgba(r, g, b, a); - } - } - - // Convert all of the edgecolors up front - typedef std::vector<agg::rgba> edgecolors_t; - edgecolors_t edgecolors; - edgecolors.reserve(Nedgecolors); - for (i = 0; i < Nedgecolors; ++i) { - double r, g, b, a; - Py::SeqBase<Py::Float> edgergba(edgecolors_obj[i]); - r = Py::Float(edgergba[0]); - g = Py::Float(edgergba[1]); - b = Py::Float(edgergba[2]); - a = 1.0; - if (edgergba.size() == 4) - a = Py::Float(edgergba[3]); - edgecolors.push_back(agg::rgba(r, g, b, a)); - } - // Convert all the dashes up front typedef std::vector<std::pair<double, GCAgg::dash_t> > dashes_t; dashes_t dashes; @@ -1039,6 +1012,11 @@ set_clipbox(cliprect, theRasterizer); bool has_clippath = render_clippath(clippath, clippath_trans); + // Set some defaults, assuming no face or edge + gc.linewidth = 0.0; + facepair_t face; + face.first = Nfacecolors != 0; + for (i = 0; i < N; ++i) { PathIterator path(paths[i % Npaths]); bool snap = (path.total_vertices() == 2); @@ -1047,11 +1025,26 @@ offset_trans.transform(&xo, &yo); agg::trans_affine_translation transOffset(xo, yo); agg::trans_affine& trans = transforms[i % Ntransforms]; - facepair_t& face = facecolors[i % Nfacecolors]; - gc.color = edgecolors[i % Nedgecolors]; - gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; - gc.dashes = dashes[i % Nlinestyles].second; - gc.dashOffset = dashes[i % Nlinestyles].first; + + if (Nfacecolors) { + size_t fi = i % Nfacecolors; + face.second = agg::rgba(*(double*)PyArray_GETPTR2(facecolors, fi, 0), + *(double*)PyArray_GETPTR2(facecolors, fi, 1), + *(double*)PyArray_GETPTR2(facecolors, fi, 2), + *(double*)PyArray_GETPTR2(facecolors, fi, 3)); + } + + if (Nedgecolors) { + size_t ei = i % Nedgecolors; + gc.color = agg::rgba(*(double*)PyArray_GETPTR2(edgecolors, ei, 0), + *(double*)PyArray_GETPTR2(edgecolors, ei, 1), + *(double*)PyArray_GETPTR2(edgecolors, ei, 2), + *(double*)PyArray_GETPTR2(edgecolors, ei, 3)); + gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; + gc.dashes = dashes[i % Nlinestyles].second; + gc.dashOffset = dashes[i % Nlinestyles].first; + } + gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); _draw_path(path, trans * transOffset, snap, has_clippath, face, gc); } @@ -1400,16 +1393,17 @@ template<class T> bool point_in_path_impl(double tx, double ty, T& path) { int yflag0, yflag1, inside_flag; - double vtx0, vty0, vtx1, vty1; + double vtx0, vty0, vtx1, vty1, sx, sy; double x, y; path.rewind(0); - if (path.vertex(&x, &y) == agg::path_cmd_stop) + unsigned code = path.vertex(&x, &y); + if (code == agg::path_cmd_stop) return false; while (true) { - vtx0 = x; - vty0 = y; + sx = vtx0 = x; + sy = vty0 = y; // get test bit for above/below X axis yflag0 = (vty0 >= ty); @@ -1419,14 +1413,13 @@ inside_flag = 0; while (true) { - unsigned code = path.vertex(&x, &y); - if (code == agg::path_cmd_stop) - return false; + code = path.vertex(&x, &y); + // The following cases denote the beginning on a new subpath - if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) + if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { + x = sx; y = sy; + } else if (code == agg::path_cmd_move_to) break; - if (code == agg::path_cmd_move_to) - break; yflag1 = (vty1 >= ty); // Check if endpoints straddle (are on opposite sides) of X axis @@ -1459,10 +1452,17 @@ vtx1 = x; vty1 = y; + + if (code == agg::path_cmd_stop || + (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) + break; } if (inside_flag != 0) return true; + + if (code == agg::path_cmd_stop) + return false; } return false; @@ -1641,15 +1641,18 @@ agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[3]); Py::SeqBase<Py::Object> paths = args[4]; Py::SeqBase<Py::Object> transforms_obj = args[5]; - Py::SeqBase<Py::Object> offsets = args[6]; - agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7], false); - Py::SeqBase<Py::Object> facecolors = args[8]; + Py::SeqBase<Py::Object> offsets_obj = args[6]; + agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7]); + bool filled = Py::Int(args[8]); + PyArrayObject* offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!offsets || offsets->dimensions[1] != 2) + throw Py::ValueError("Offsets array must be Nx2"); + size_t Npaths = paths.length(); - size_t Noffsets = offsets.length(); + size_t Noffsets = offsets->dimensions[0]; size_t N = std::max(Npaths, Noffsets); size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t Ncolors = facecolors.length(); size_t i; // Convert all of the transforms up front @@ -1668,19 +1671,18 @@ for (i = 0; i < N; ++i) { PathIterator path(paths[i % Npaths]); - Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % Noffsets]); - double xo = Py::Float(offset[0]); - double yo = Py::Float(offset[1]); + double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); + double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); offset_trans.transform(&xo, &yo); agg::trans_affine_translation transOffset(xo, yo); - agg::trans_affine trans = transforms[i % Ntransforms] * transOffset; + agg::trans_affine trans = transforms[i % Ntransforms]; + trans *= transOffset; - const Py::Object& facecolor_obj = facecolors[i & Ncolors]; - if (facecolor_obj.ptr() == Py_None) { - if (::point_on_path(x, y, radius, path, trans)) + if (filled) { + if (::point_in_path(x, y, path, trans)) result.append(Py::Int((int)i)); } else { - if (::point_in_path(x, y, path, trans)) + if (::point_on_path(x, y, radius, path, trans)) result.append(Py::Int((int)i)); } } Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-10-10 13:38:32 UTC (rev 3933) +++ branches/transforms/src/_backend_agg.h 2007-10-11 15:42:53 UTC (rev 3934) @@ -247,7 +247,7 @@ add_varargs_method("get_path_collection_extents", &_backend_agg_module::get_path_collection_extents, "get_path_collection_extents(trans, paths, transforms, offsets, offsetTrans)"); add_varargs_method("point_in_path_collection", &_backend_agg_module::point_in_path_collection, - "point_in_path_collection(x, y, r, trans, paths, transforms, offsets, offsetTrans, colors)"); + "point_in_path_collection(x, y, r, trans, paths, transforms, offsets, offsetTrans, filled)"); initialize( "The agg rendering backend" ); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-10 13:38:38
|
Revision: 3933 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3933&view=rev Author: mdboom Date: 2007-10-10 06:38:32 -0700 (Wed, 10 Oct 2007) Log Message: ----------- Merged revisions 3929-3932 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3930 | mdboom | 2007-10-09 11:58:09 -0400 (Tue, 09 Oct 2007) | 2 lines Fix bug saving PNG files in GtkAgg backend. ........ r3931 | mdboom | 2007-10-09 11:58:36 -0400 (Tue, 09 Oct 2007) | 3 lines Raw files should be saved (optionally) with rgba extension, which is what ImageMagick understands. ........ Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_gtkagg.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3928 + /trunk/matplotlib:1-3932 Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-10 13:37:28 UTC (rev 3932) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-10 13:38:32 UTC (rev 3933) @@ -1083,7 +1083,7 @@ 'png': 'Portable Network Graphics', 'ps' : 'Postscript', 'raw': 'Raw RGBA bitmap', - 'rgb': 'Raw RGBA bitmap', + 'rgba': 'Raw RGBA bitmap', 'svg': 'Scalable Vector Graphics', 'svgz': 'Scalable Vector Graphics' } Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-10-10 13:37:28 UTC (rev 3932) +++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-10-10 13:38:32 UTC (rev 3933) @@ -98,7 +98,9 @@ if DEBUG: print 'FigureCanvasGTKAgg.done' def print_png(self, filename, *args, **kwargs): - return FigureCanvasAgg.print_png(self, filename, *args, **kwargs) + # Do this so we can save the resolution of figure in the PNG file + agg = self.switch_backends(FigureCanvasAgg) + return agg.print_png(filename, *args, **kwargs) """\ Traceback (most recent call last): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-10 13:37:31
|
Revision: 3932 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3932&view=rev Author: mdboom Date: 2007-10-10 06:37:28 -0700 (Wed, 10 Oct 2007) Log Message: ----------- Lots more work on making examples work. Details, details, details... Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/colorbar.py branches/transforms/lib/matplotlib/dates.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/scale.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/ticker.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -477,7 +477,11 @@ """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} martist.Artist.__init__(self) - self._position = mtransforms.Bbox.from_lbwh(*rect) + if isinstance(rect, mtransforms.Bbox): + self._position = rect + else: + warnings.warn("Passing non-bbox as rect to Axes") + mtransforms.Bbox.from_lbwh(*rect) self._originalPosition = self._position.frozen() self.set_axes(self) self.set_aspect('auto') @@ -564,16 +568,16 @@ # It is assumed that this part will have non-linear components self.transScale = mtransforms.TransformWrapper(mtransforms.IdentityTransform()) - # A (possibly non-linear) projection on the (already scaled) data - self.transProjection = mtransforms.IdentityTransform() - # An affine transformation on the data, generally to limit the # range of the axes self.transLimits = mtransforms.BboxTransform( mtransforms.TransformedBbox(self.viewLim, self.transScale), mtransforms.Bbox.unit()) - - self.transData = self.transScale + self.transProjection + self.transLimits + self.transAxes + # The parentheses are important for efficiency here -- they + # group the last two (which are usually affines) separately + # from the first (which, with log-scaling can be non-affine). + self.transData = self.transScale + (self.transLimits + self.transAxes) + self._xaxis_transform = mtransforms.blended_transform_factory( self.axes.transData, self.axes.transAxes) self._yaxis_transform = mtransforms.blended_transform_factory( @@ -807,9 +811,9 @@ ', '.join(mtransforms.BBox.coefs.keys())) def get_data_ratio(self): - xmin,xmax = self.get_xlim() + xmin,xmax = self.get_xbound() xsize = max(math.fabs(xmax-xmin), 1e-30) - ymin,ymax = self.get_ylim() + ymin,ymax = self.get_ybound() ysize = max(math.fabs(ymax-ymin), 1e-30) return ysize/xsize @@ -819,7 +823,7 @@ axes box or the view limits. ''' #MGDTODO: Numpify - + if self._aspect == 'auto': self.set_position( self._originalPosition , 'active') return @@ -843,7 +847,6 @@ self.set_position(pb1.anchored(self._anchor, pb), 'active') return - xmin,xmax = self.get_xbound() xsize = max(math.fabs(xmax-xmin), 1e-30) ymin,ymax = self.get_ybound() @@ -1519,7 +1522,9 @@ if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax - xmax, xmin = mtransforms.nonsingular(xmax, xmin, increasing=False) + xmin, xmax = mtransforms.nonsingular(xmin, xmax) + xmin, xmax = self.xaxis.limit_range_for_scale(xmin, xmax) + self.viewLim.intervalx = (xmin, xmax) if emit: @@ -1678,6 +1683,7 @@ if ymax is None: ymax = old_ymax ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) + ymin, ymax = self.yaxis.limit_range_for_scale(ymin, ymax) self.viewLim.intervaly = (ymin, ymax) if emit: @@ -1884,10 +1890,12 @@ .transformed(p.trans_inverse) elif button == 3: try: - # MGDTODO: This is broken with log scales - dx, dy = format_deltas(key, dx, dy) dx = -dx / float(self.bbox.width) dy = -dy / float(self.bbox.height) + dx, dy = format_deltas(key, dx, dy) + if self.get_aspect() != 'auto': + dx = 0.5 * (dx + dy) + dy = dx xmin, ymin, xmax, ymax = p.lim.lbrt alpha = npy.power(10.0, (dx, dy)) @@ -2375,7 +2383,7 @@ axvspan.__doc__ = cbook.dedent(axvspan.__doc__) % martist.kwdocd - def hlines(self, y, xmin, xmax, colors='k', linestyle='solid', + def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): """ HLINES(y, xmin, xmax, colors='k', linestyle='solid', **kwargs) @@ -2417,7 +2425,7 @@ verts = [ ((thisxmin, thisy), (thisxmax, thisy)) for thisxmin, thisxmax, thisy in zip(xmin, xmax, y)] coll = mcoll.LineCollection(verts, colors=colors, - linestyle=linestyle, label=label) + linestyles=linestyles, label=label) self.add_collection(coll) coll.update(kwargs) @@ -4896,7 +4904,7 @@ self.set_xlabel('Frequency') self.set_ylabel('Cross Spectrum Magnitude (dB)') self.grid(True) - vmin, vmax = self.viewLim.intervaly().get_bounds() + vmin, vmax = self.viewLim.intervaly intv = vmax-vmin step = 10*int(npy.log10(intv)) @@ -5177,8 +5185,7 @@ self.update_params() # _axes_class is set in the subplot_class_factory - self._axes_class.__init__(self, fig, [self.figLeft, self.figBottom, - self.figW, self.figH], **kwargs) + self._axes_class.__init__(self, fig, self.figbox, **kwargs) def get_geometry(self): 'get the subplot geometry, eg 2,2,3' @@ -5190,7 +5197,7 @@ self._cols = numcols self._num = num-1 self.update_params() - self.set_position([self.figLeft, self.figBottom, self.figW, self.figH]) + self.set_position(self.figbox) def update_params(self): 'update the subplot position from fig.subplotpars' @@ -5220,10 +5227,7 @@ figBottom = top - (rowNum+1)*figH - rowNum*sepH figLeft = left + colNum*(figW + sepW) - self.figBottom = figBottom - self.figLeft = figLeft - self.figW = figW - self.figH = figH + self.figbox = mtransforms.Bbox.from_lbwh(figLeft, figBottom, figW, figH) self.rowNum = rowNum self.colNum = colNum self.numRows = rows Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -524,7 +524,10 @@ def set_scale(self, value, **kwargs): self._scale = scale_factory(value, self, **kwargs) self._scale.set_default_locators_and_formatters(self) - + + def limit_range_for_scale(self, vmin, vmax): + return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) + def get_children(self): children = [self.label] majorticks = self.get_major_ticks() @@ -580,6 +583,10 @@ 'return the Interval instance for this axis data limits' raise NotImplementedError('Derived must override') + def set_data_interval(self): + 'Set the axis data limits' + raise NotImplementedError('Derived must override') + def _set_artist_props(self, a): if a is None: return a.set_figure(self.figure) @@ -887,7 +894,7 @@ ACCEPTS: A Formatter instance """ self.major.formatter = formatter - self.major.formatter.set_axis(self) + formatter.set_axis(self) def set_minor_formatter(self, formatter): @@ -897,7 +904,7 @@ ACCEPTS: A Formatter instance """ self.minor.formatter = formatter - self.minor.formatter.set_axis(self) + formatter.set_axis(self) def set_major_locator(self, locator): @@ -907,7 +914,7 @@ ACCEPTS: a Locator instance """ self.major.locator = locator - self.major.locator.set_axis(self) + locator.set_axis(self) def set_minor_locator(self, locator): @@ -917,7 +924,7 @@ ACCEPTS: a Locator instance """ self.minor.locator = locator - self.minor.locator.set_axis(self) + locator.set_axis(self) def set_pickradius(self, pickradius): """ @@ -1180,7 +1187,15 @@ 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervalx + def set_data_interval(self, vmin, vmax, ignore=False): + 'return the Interval instance for this axis data limits' + if ignore: + self.axes.dataLim.intervalx = vmin, vmax + else: + Vmin, Vmax = self.get_data_interval() + self.axes.dataLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) + class YAxis(Axis): __name__ = 'yaxis' axis_name = 'y' @@ -1395,5 +1410,12 @@ 'return the Interval instance for this axis data limits' return self.axes.dataLim.intervaly + def set_data_interval(self, vmin, vmax, ignore=False): + 'return the Interval instance for this axis data limits' + if ignore: + self.axes.dataLim.intervaly = vmin, vmax + else: + Vmin, Vmax = self.get_data_interval() + self.axes.dataLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -1598,7 +1598,7 @@ for a, ind in self._xypress: a.end_pan() if not self._xypress: return - self._xypress = None + self._xypress = [] self._button_pressed=None self.push_current() self.release(event) Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -381,11 +381,11 @@ return 'png' def print_raw(self, filename, *args, **kwargs): - self.draw() + FigureCanvasAgg.draw(self) self.get_renderer()._renderer.write_rgba(str(filename)) print_rgba = print_raw def print_png(self, filename, *args, **kwargs): - self.draw() - self.get_renderer()._renderer.write_png(str(filename), self.figure.dpi.get()) + FigureCanvasAgg.draw(self) + self.get_renderer()._renderer.write_png(str(filename), self.figure.dpi) Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/cbook.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -1051,7 +1051,6 @@ a1 = a[1: ] delta = ((a1 - a0) / steps) - # MGDTODO: Could use linspace here? for i in range(1, int(steps)): result[i::steps] = delta * i + a0 result[steps::steps] = a1 Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -59,12 +59,6 @@ _offsets = npy.zeros((1, 2)) _transOffset = transforms.IdentityTransform() - _facecolors = [None] - _edgecolors = [None] - _lw = [1.0] - _ls = [None] - _aa = [True] - _pickradius = 5.0 _transforms = [None] zorder = 1 @@ -107,9 +101,8 @@ self._uniform_offsets = None self._offsets = npy.zeros((1, 2)) if offsets is not None: - offsets = npy.asarray(offsets) - if len(offsets.shape) == 1: - offsets = offsets[npy.newaxis,:] # Make it Nx2. +# if len(offsets.shape) == 1: +# offsets = offsets[npy.newaxis,:] # Make it Nx2. if transOffset is not None: Affine2D = transforms.Affine2D self._offsets = offsets @@ -152,29 +145,37 @@ transform = self.get_transform() transOffset = self._transOffset offsets = self._offsets + paths = self.get_paths() - # MGDTODO: Transform the paths (since we don't keep track of segments anymore + # MGDTODO: Test me if self.have_units(): - segments = [] - for segment in self._segments: + paths = [] + for path in self._paths: + vertices = path.vertices xs, ys = zip(*segment) xs = self.convert_xunits(xs) ys = self.convert_yunits(ys) - segments.append(zip(xs, ys)) -# if self._offsets is not None: -# xs = self.convert_xunits(self._offsets[:0]) -# ys = self.convert_yunits(self._offsets[:1]) -# offsets = zip(xs, ys) + paths.append(path.Path(zip(xs, ys), path.codes)) + if self._offsets is not None: + xs = self.convert_xunits(self._offsets[:0]) + ys = self.convert_yunits(self._offsets[:1]) + offsets = zip(xs, ys) self.update_scalarmappable() #print 'calling renderer draw line collection' clippath, clippath_trans = self.get_transformed_clip_path_and_affine() + if not transform.is_affine: + paths = [transform.transform_path_non_affine(path) for path in paths] + transform = transform.get_affine() + renderer.draw_path_collection( transform, self.clipbox, clippath, clippath_trans, - self.get_paths(), self.get_transforms(), offsets, transOffset, - self._facecolors, self._edgecolors, self._lw, self._ls, self._aa) + paths, self.get_transforms(), + npy.asarray(offsets, npy.float_), transOffset, + self._facecolors, self._edgecolors, self._linewidths, + self._linestyles, self._antialiaseds) renderer.close_group(self.__class__.__name__) def contains(self, mouseevent): @@ -190,7 +191,7 @@ self._offsetTrans, self._facecolors) return len(ind)>0,dict(ind=ind) - # MGDTODO + # MGDTODO: Update def get_transformed_patches(self): """ get a sequence of the polygons in the collection in display (transformed) space @@ -272,7 +273,7 @@ except ValueError: raise ValueError('Do not know how to convert %s to dashes'%ls) - self._ls = dashes + self._linestyles = dashes def set_color(self, c): """ @@ -328,13 +329,13 @@ self._edgecolors = [(r,g,b,alpha) for r,g,b,a in self._edgecolors] def get_linewidth(self): - return self._lw - + return self._linewidths + def get_linestyle(self): - return self._ls + return self._linestyles def get_dashes(self): - return self._ls + return self._linestyles def update_scalarmappable(self): """ @@ -509,14 +510,20 @@ self._sizes = sizes self._dpi = dpi self._paths = [path.Path.unit_regular_polygon(numsides)] - self._transforms = [transforms.Affine2D().scale(x) for x in sizes] + # sizes is the area of the circle circumscribing the polygon + # in points^2 + self._transforms = [ + transforms.Affine2D().rotate(rotation).scale( + (math.sqrt(x) * self._dpi / 72.0) * (1.0 / math.sqrt(math.pi))) + for x in sizes] + self.set_transform(transforms.IdentityTransform()) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_paths(self): return self._paths - # MGDTODO + # MGDTODO: Update def get_transformed_patches(self): # Shouldn't need all these calls to asarray; # the variables should be converted when stored. Modified: branches/transforms/lib/matplotlib/colorbar.py =================================================================== --- branches/transforms/lib/matplotlib/colorbar.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/colorbar.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -317,12 +317,13 @@ intv = transforms.Interval(transforms.Value(self._values[0]), transforms.Value(self._values[-1])) else: - intv = transforms.Interval(transforms.Value(self.vmin), - transforms.Value(self.vmax)) - locator.set_view_interval(intv) - locator.set_data_interval(intv) - formatter.set_view_interval(intv) - formatter.set_data_interval(intv) + intv = self.vmin, self.vmax + locator.create_dummy_axis() + locator.set_view_interval(*intv) + locator.set_data_interval(*intv) + formatter.create_dummy_axis() + formatter.set_view_interval(*intv) + formatter.set_data_interval(*intv) b = npy.array(locator()) b, ticks = self._locate(b) formatter.set_locs(b) Modified: branches/transforms/lib/matplotlib/dates.py =================================================================== --- branches/transforms/lib/matplotlib/dates.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/dates.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -429,13 +429,11 @@ self.tz = tz def datalim_to_dt(self): - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() + dmin, dmax = self.axis.get_data_interval() return num2date(dmin, self.tz), num2date(dmax, self.tz) def viewlim_to_dt(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() + vmin, vmax = self.axis.get_view_interval() return num2date(vmin, self.tz), num2date(vmax, self.tz) def _get_unit(self): @@ -459,8 +457,6 @@ self.rule = o def __call__(self): - self.verify_intervals() - # if no data have been set, this will tank with a ValueError try: dmin, dmax = self.viewlim_to_dt() except ValueError: return [] @@ -500,7 +496,6 @@ """ Set the view limits to include the data range """ - self.verify_intervals() dmin, dmax = self.datalim_to_dt() if dmin>dmax: dmax, dmin = dmin, dmax @@ -537,6 +532,10 @@ self.refresh() return self._locator() + def set_axis(self, axis): + DateLocator.set_axis(self, axis) + self._locator.set_axis(axis) + def refresh(self): 'refresh internal information based on current lim' dmin, dmax = self.viewlim_to_dt() @@ -563,8 +562,6 @@ def autoscale(self): 'Try to choose the view limits intelligently' - - self.verify_intervals() dmin, dmax = self.datalim_to_dt() self._locator = self.get_locator(dmin, dmax) return self._locator.autoscale() @@ -667,9 +664,10 @@ bysecond=bysecond ) locator = RRuleLocator(rrule, self.tz) - - locator.set_view_interval(self.viewInterval) - locator.set_data_interval(self.dataInterval) + locator.set_axis(self.axis) + + locator.set_view_interval(*self.axis.get_view_interval()) + locator.set_data_interval(*self.axis.get_data_interval()) return locator @@ -710,8 +708,6 @@ return 365 def __call__(self): - self.verify_intervals() - dmin, dmax = self.viewlim_to_dt() ymin = self.base.le(dmin.year) ymax = self.base.ge(dmax.year) @@ -728,7 +724,6 @@ """ Set the view limits to include the data range """ - self.verify_intervals() dmin, dmax = self.datalim_to_dt() ymin = self.base.le(dmin.year) Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/figure.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -818,17 +818,17 @@ self.subplotpars.update(*args, **kwargs) import matplotlib.axes for ax in self.axes: - if not isinstance(ax, matplotlib.axes.Subplot): + if not isinstance(ax, matplotlib.axes.SubplotBase): # Check if sharing a subplots axis - if ax._sharex is not None and isinstance(ax._sharex, matplotlib.axes.Subplot): + if ax._sharex is not None and isinstance(ax._sharex, matplotlib.axes.SubplotBase): ax._sharex.update_params() - ax.set_position([ax._sharex.figLeft, ax._sharex.figBottom, ax._sharex.figW, ax._sharex.figH]) - elif ax._sharey is not None and isinstance(ax._sharey, matplotlib.axes.Subplot): + ax.set_position(ax._sharex.figbox) + elif ax._sharey is not None and isinstance(ax._sharey, matplotlib.axes.SubplotBase): ax._sharey.update_params() - ax.set_position([ax._sharey.figLeft, ax._sharey.figBottom, ax._sharey.figW, ax._sharey.figH]) + ax.set_position(ax._sharey.figbox) else: ax.update_params() - ax.set_position([ax.figLeft, ax.figBottom, ax.figW, ax.figH]) + ax.set_position(ax.figbox) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -300,18 +300,19 @@ raise ValueError,"pick radius should be a distance" # transform in backend - x = self._x - y = self._y - if len(x)==0: return False,{} + if len(self._xy)==0: return False,{} - xt, yt = self.get_transform().numerix_x_y(x, y) - + xyt = self.get_transform().transform(self._xy) + xt = xyt[:, 0] + yt = xyt[:, 1] + if self.figure == None: print str(self),' has no figure set' pixels = self.pickradius else: - pixels = self.figure.dpi.get()/72. * self.pickradius - + pixels = self.figure.dpi/72. * self.pickradius + + path, transform = self._transformed_path.get_transformed_path_and_affine() if self._linestyle == 'None': # If no line, return the nearby point(s) d = npy.sqrt((xt-mouseevent.x)**2 + (yt-mouseevent.y)**2) @@ -384,11 +385,17 @@ def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units - x = ma.asarray(self.convert_xunits(self._xorig), float) - y = ma.asarray(self.convert_yunits(self._yorig), float) - - x = ma.ravel(x) - y = ma.ravel(y) + if ma.isMaskedArray(self._xorig) or ma.isMaskedArray(self._yorig): + x = ma.asarray(self.convert_xunits(self._xorig), float) + y = ma.asarray(self.convert_yunits(self._yorig), float) + x = ma.ravel(x) + y = ma.ravel(y) + else: + x = npy.asarray(self.convert_xunits(self._xorig), float) + y = npy.asarray(self.convert_yunits(self._yorig), float) + x = npy.ravel(x) + y = npy.ravel(y) + if len(x)==1 and len(y)>1: x = x * npy.ones(y.shape, float) if len(y)==1 and len(x)>1: @@ -399,8 +406,11 @@ x = x.reshape((len(x), 1)) y = y.reshape((len(y), 1)) - - self._xy = ma.concatenate((x, y), 1) + + if ma.isMaskedArray(x) or ma.isMaskedArray(y): + self._xy = ma.concatenate((x, y), 1) + else: + self._xy = npy.concatenate((x, y), 1) self._x = self._xy[:, 0] # just a view self._y = self._xy[:, 1] # just a view self._logcache = None Modified: branches/transforms/lib/matplotlib/scale.py =================================================================== --- branches/transforms/lib/matplotlib/scale.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/scale.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -14,7 +14,10 @@ class ScaleBase(object): def set_default_locators_and_formatters(self, axis): raise NotImplementedError - + + def limit_range_for_scale(self, vmin, vmax, minpos): + return vmin, vmax + class LinearScale(ScaleBase): name = 'linear' @@ -39,6 +42,9 @@ is_separable = True def transform(self, a): + # MGDTODO: Remove me + if len(a) > 10: + print "log transforming" return ma.log10(ma.masked_where(a <= 0.0, a * 10.0)) def inverted(self): @@ -159,6 +165,9 @@ def get_transform(self): return self._transform + def limit_range_for_scale(self, vmin, vmax, minpos): + return (vmin <= 0.0 and minpos or vmin, + vmax <= 0.0 and minpos or vmax) _scale_mapping = { 'linear' : LinearScale, Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/text.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -179,14 +179,17 @@ key = self.get_prop_tup() if self.cached.has_key(key): return self.cached[key] horizLayout = [] - thisx, thisy = self._get_xy_display() + transform = self.get_transform() + x, y = self.get_position() + thisx, thisy = transform.transform_point((x, y)) + tx, ty = thisx, thisy + width = 0 height = 0 xmin, ymin = thisx, thisy lines = self._text.split('\n') - # MGDTODO: whs could be a numpy.array whs = [] # Find full vertical extent of font, # including ascenders and descenders: @@ -267,11 +270,10 @@ # now rotate the positions around the first x,y position xys = M.transform(offsetLayout) - xys[:, 0] += offsetx - xys[:, 1] += offsety + xys += (offsetx, offsety) # now inverse transform back to data coords - inverse_transform = self.get_transform().inverted() + inverse_transform = transform.inverted() xys = inverse_transform.transform(xys) xs, ys = xys[:, 0], xys[:, 1] @@ -781,13 +783,14 @@ # Compute the dash end points # The 'c' prefix is for canvas coordinates - cxy = npy.array(transform.xy_tup((dashx, dashy))) + cxy = transform.transform_point((dashx, dashy)) cd = npy.array([cos_theta, sin_theta]) c1 = cxy+dashpush*cd c2 = cxy+(dashpush+dashlength)*cd - (x1, y1) = transform.inverse_xy_tup(tuple(c1)) - (x2, y2) = transform.inverse_xy_tup(tuple(c2)) + inverse = transform.inverted() + (x1, y1) = inverse.transform_point(tuple(c1)) + (x2, y2) = inverse.transform_point(tuple(c2)) self.dashline.set_data((x1, x2), (y1, y2)) # We now need to extend this vector out to @@ -805,7 +808,7 @@ # but I don't grok the transformation stuff # well enough yet. we = Text.get_window_extent(self, renderer=renderer) - w, h = we.width(), we.height() + w, h = we.width, we.height # Watch for zeros if sin_theta == 0.0: dx = w @@ -824,13 +827,13 @@ cwd *= 1+dashpad/npy.sqrt(npy.dot(cwd,cwd)) cw = c2+(dashdirection*2-1)*cwd - self._x, self._y = transform.inverse_xy_tup(tuple(cw)) + self._x, self._y = inverse.transform_point(tuple(cw)) # Now set the window extent # I'm not at all sure this is the right way to do this. we = Text.get_window_extent(self, renderer=renderer) - self._twd_window_extent = we.deepcopy() - self._twd_window_extent.update(((c1[0], c1[1]),), False) + self._twd_window_extent = we.frozen() + self._twd_window_extent.update_from_data_xy(npy.array([c1]), False) # Finally, make text align center Text.set_horizontalalignment(self, 'center') Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/ticker.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -116,10 +116,35 @@ class TickHelper: axis = None + class DummyAxis: + def __init__(self): + self.dataLim = mtransforms.Bbox + self.viewLim = mtransforms.Bbox + + def get_view_interval(self): + return self.viewLim.intervalx + + def set_view_interval(self, vmin, vmax): + self.viewLim.intervalx = vmin, vmax + + def get_data_interval(self): + return self.dataLim.intervalx + + def set_data_interval(self, vmin, vmax): + self.dataLim.intervalx = vmin, vmax + def set_axis(self, axis): self.axis = axis + def create_dummy_axis(self): + self.axis = self.DummyAxis() + + def set_view_interval(self, vmin, vmax): + self.axis.set_view_interval(vmin, vmax) + def set_data_interval(self, vmin, vmax): + self.axis.set_data_interval(vmin, vmax) + class Formatter(TickHelper): """ Convert the tick location to a string @@ -900,6 +925,9 @@ vmin, vmax = self.axis.get_view_interval() if vmin <= 0.0: vmin = self.axis.get_minpos() + if vmin <= 0.0: + raise ValueError( + "Data has no positive values, and therefore can not be log-scaled.") vmin = math.log(vmin)/math.log(b) vmax = math.log(vmax)/math.log(b) @@ -936,7 +964,8 @@ minpos = self.axis.get_minpos() if minpos<=0: - raise RuntimeError('No positive data to plot') + raise ValueError( + "Data has no positive values, and therefore can not be log-scaled.") if vmin <= minpos: vmin = minpos Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-10 13:37:28 UTC (rev 3932) @@ -32,7 +32,7 @@ import cbook from path import Path -DEBUG = False +DEBUG = True class TransformNode(object): """ @@ -403,7 +403,6 @@ if len(x) == 0 or len(y) == 0: return - # MGDTODO: All getters of minpos should be aware that is is sometimes -inf if ma.isMaskedArray(x) or ma.isMaskedArray(y): xpos = ma.where(x > 0.0, x, npy.inf) ypos = ma.where(y > 0.0, y, npy.inf) @@ -616,26 +615,28 @@ #@staticmethod def union(bboxes): """ - Return a Bbox that bounds all the given bboxes. + Return a Bbox that contains all of the given bboxes. """ - # MGDTODO: There's got to be a way to utilize numpy here - # to make this faster... assert(len(bboxes)) if len(bboxes) == 1: return bboxes[0] - - bbox = bboxes[0] - xmin0, ymin0, xmax0, ymax0 = bbox.lbrt - for bbox in bboxes[1:]: - xmin, ymin, xmax, ymax = bbox.lbrt - xmin0 = min(xmin0, xmin) - ymin0 = min(ymin0, ymin) - xmax0 = max(xmax0, xmax) - ymax0 = max(ymax0, ymax) + xmin = npy.inf + ymin = npy.inf + xmax = -npy.inf + ymax = -npy.inf - return Bbox.from_lbrt(xmin0, ymin0, xmax0, ymax0) + for bbox in bboxes: + points = bbox.get_points() + xs = points[:, 0] + ys = points[:, 1] + xmin = min(xmin, npy.min(xs)) + ymin = min(ymin, npy.min(ys)) + xmax = max(xmax, npy.max(xs)) + ymax = max(ymax, npy.max(ys)) + + return Bbox.from_lbrt(xmin, ymin, xmax, ymax) union = staticmethod(union) @@ -1243,22 +1244,18 @@ # MGDTODO: The major speed trap here is just converting to # the points to an array in the first place. If we can use # more arrays upstream, that should help here. - if DEBUG and not isinstance(values, npy.ndarray): + if DEBUG and not ma.isMaskedArray(points) and not isinstance(points, npy.ndarray): import traceback print '-' * 60 - print 'A non-numpy array of type %s was passed in for transformation.' % type(values) + print 'A non-numpy array of type %s was passed in for transformation.' % type(points) print 'Please correct this.' print "".join(traceback.format_stack()) mtx = self.get_matrix() if ma.isMaskedArray(points): - points = points.transpose() - points = ma.dot(mtx[0:2, 0:2], points) - points = points + mtx[0:2, 2:] + points = ma.dot(mtx[0:2, 0:2], points.transpose()) + mtx[0:2, 2:] else: - points = npy.asarray(points, npy.float_) - points = points.transpose() - points = npy.dot(mtx[0:2, 0:2], points) - points = points + mtx[0:2, 2:] + # points = npy.asarray(points, npy.float_) + points = npy.dot(mtx[0:2, 0:2], points.transpose()) + mtx[0:2, 2:] return points.transpose() transform.__doc__ = AffineBase.transform.__doc__ @@ -1558,7 +1555,10 @@ y_points = y.transform(points[:, 1]) y_points = y_points.reshape((len(y_points), 1)) - return ma.concatenate((x_points, y_points), 1) + if ma.isMaskedArray(x_points) or ma.isMaskedArray(y_points): + return ma.concatenate((x_points, y_points), 1) + else: + return npy.concatenate((x_points, y_points), 1) transform.__doc__ = Transform.transform.__doc__ transform_non_affine = transform @@ -1741,14 +1741,6 @@ return "CompositeGenericTransform(%s, %s)" % (self._a, self._b) __str__ = __repr__ - # MGDTODO: Remove -# def get_matrix(self): -# if self._invalid: -# assert self._a.is_affine and self._b.is_affine -# self._mtx = npy.dot(self._b.get_matrix(), self._a.get_matrix()) -# self._invalid = 0 -# return self._mtx - def transform(self, points): return self._b.transform( self._a.transform(points)) @@ -1927,18 +1919,18 @@ the transform already applied, along with the affine part of the path necessary to complete the transformation. """ - if (self._invalid != self.INVALID_AFFINE or + if (self._invalid & self.INVALID_NON_AFFINE or self._transformed_path is None): self._transformed_path = \ self._transform.transform_path_non_affine(self._path) - self._invalid = 0 + self._invalid = 0 return self._transformed_path, self._transform.get_affine() def get_fully_transformed_path(self): """ Return a fully-transformed copy of the child path. """ - if (self._invalid != self.INVALID_AFFINE + if (self._invalid & self.INVALID_NON_AFFINE or self._transformed_path is None): self._transformed_path = \ self._transform.transform_path_non_affine(self._path) Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-09 15:58:36 UTC (rev 3931) +++ branches/transforms/src/_backend_agg.cpp 2007-10-10 13:37:28 UTC (rev 3932) @@ -527,10 +527,8 @@ RendererAgg::draw_markers(const Py::Tuple& args) { typedef agg::conv_transform<PathIterator> transformed_path_t; typedef conv_quantize<transformed_path_t> quantize_t; - typedef agg::conv_curve<transformed_path_t> curve_t; + typedef agg::conv_curve<transformed_path_t> curve_t; typedef agg::conv_stroke<curve_t> stroke_t; - typedef agg::conv_dash<curve_t> dash_t; - typedef agg::conv_stroke<dash_t> stroke_dash_t; typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; @@ -836,16 +834,30 @@ // Render face if (face.first) { - if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); - ren.color(face.second); - agg::render_scanlines(*theRasterizer, *slineP8, ren); + if (gc.isaa) { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(face.second); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererAA->color(face.second); + theRasterizer->add_path(curve); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } } else { - rendererAA->color(face.second); - theRasterizer->add_path(curve); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_bin_renderer_type ren(r); + ren.color(face.second); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererBin->color(face.second); + theRasterizer->add_path(curve); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererBin); + } } } @@ -959,7 +971,7 @@ size_t Naa = antialiaseds.length(); size_t i = 0; - + // Convert all of the transforms up front typedef std::vector<agg::trans_affine> transforms_t; transforms_t transforms; @@ -1028,6 +1040,8 @@ bool has_clippath = render_clippath(clippath, clippath_trans); for (i = 0; i < N; ++i) { + PathIterator path(paths[i % Npaths]); + bool snap = (path.total_vertices() == 2); double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); offset_trans.transform(&xo, &yo); @@ -1039,8 +1053,6 @@ gc.dashes = dashes[i % Nlinestyles].second; gc.dashOffset = dashes[i % Nlinestyles].first; gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); - PathIterator path(paths[i % Npaths]); - bool snap = (path.total_vertices() == 2); _draw_path(path, trans * transOffset, snap, has_clippath, face, gc); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-09 15:58:37
|
Revision: 3931 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3931&view=rev Author: mdboom Date: 2007-10-09 08:58:36 -0700 (Tue, 09 Oct 2007) Log Message: ----------- Raw files should be saved (optionally) with rgba extension, which is what ImageMagick understands. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backend_bases.py Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2007-10-09 15:58:09 UTC (rev 3930) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2007-10-09 15:58:36 UTC (rev 3931) @@ -1079,7 +1079,7 @@ 'png': 'Portable Network Graphics', 'ps' : 'Postscript', 'raw': 'Raw RGBA bitmap', - 'rgb': 'Raw RGBA bitmap', + 'rgba': 'Raw RGBA bitmap', 'svg': 'Scalable Vector Graphics', 'svgz': 'Scalable Vector Graphics' } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-09 15:58:11
|
Revision: 3930 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3930&view=rev Author: mdboom Date: 2007-10-09 08:58:09 -0700 (Tue, 09 Oct 2007) Log Message: ----------- Fix bug saving PNG files in GtkAgg backend. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2007-10-08 18:15:05 UTC (rev 3929) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2007-10-09 15:58:09 UTC (rev 3930) @@ -98,7 +98,9 @@ if DEBUG: print 'FigureCanvasGTKAgg.done' def print_png(self, filename, *args, **kwargs): - return FigureCanvasAgg.print_png(self, filename, *args, **kwargs) + # Do this so we can save the resolution of figure in the PNG file + agg = self.switch_backends(FigureCanvasAgg) + return agg.print_png(filename, *args, **kwargs) """\ Traceback (most recent call last): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-08 18:15:08
|
Revision: 3929 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3929&view=rev Author: mdboom Date: 2007-10-08 11:15:05 -0700 (Mon, 08 Oct 2007) Log Message: ----------- Merged revisions 3925-3928 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3926 | efiring | 2007-10-05 18:11:32 -0400 (Fri, 05 Oct 2007) | 2 lines Fixed numpification bug in pcolor argument handling ........ r3927 | mdboom | 2007-10-08 08:45:23 -0400 (Mon, 08 Oct 2007) | 2 lines Save image resolution in the PNG file. ........ Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/backends/backend_gtkagg.py branches/transforms/src/_backend_agg.cpp Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3924 + /trunk/matplotlib:1-3928 Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-08 18:10:11 UTC (rev 3928) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-08 18:15:05 UTC (rev 3929) @@ -4390,7 +4390,30 @@ return im + def _pcolorargs(self, funcname, *args): + if len(args)==1: + C = args[0] + numRows, numCols = C.shape + X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) + elif len(args)==3: + X, Y, C = args + else: + raise TypeError( + 'Illegal arguments to %s; see help(%s)' % (funcname, funcname)) + Nx = X.shape[-1] + Ny = Y.shape[0] + if len(X.shape) <> 2 or X.shape[0] == 1: + x = X.reshape(1,Nx) + X = x.repeat(Ny, axis=0) + if len(Y.shape) <> 2 or Y.shape[1] == 1: + y = Y.reshape(Ny, 1) + Y = y.repeat(Nx, axis=1) + if X.shape != Y.shape: + raise TypeError( + 'Incompatible X, Y inputs to %s; see help(%s)' % (funcname, funcname)) + return X, Y, C + def pcolor(self, *args, **kwargs): """ pcolor(*args, **kwargs): pseudocolor plot of a 2-D array @@ -4502,25 +4525,9 @@ vmax = kwargs.pop('vmax', None) shading = kwargs.pop('shading', 'flat') - if len(args)==1: - C = args[0] - numRows, numCols = C.shape - X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) - elif len(args)==3: - X, Y, C = args - numRows, numCols = C.shape - else: - raise TypeError, 'Illegal arguments to pcolor; see help(pcolor)' + X, Y, C = self._pcolorargs('pcolor', *args) + Ny, Nx = X.shape - Nx = X.shape[-1] - Ny = Y.shape[0] - if len(X.shape) <> 2 or X.shape[0] == 1: - X = X.ravel().resize((Ny, Nx)) - if len(Y.shape) <> 2 or Y.shape[1] == 1: - Y = Y.ravel().resize((Nx, Ny)).T - - - # convert to MA, if necessary. C = ma.asarray(C) X = ma.asarray(X) @@ -4655,23 +4662,9 @@ shading = kwargs.pop('shading', 'flat') edgecolors = kwargs.pop('edgecolors', 'None') - if len(args)==1: - C = args[0] - numRows, numCols = C.shape - X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) - elif len(args)==3: - X, Y, C = args - numRows, numCols = C.shape - else: - raise TypeError, 'Illegal arguments to pcolormesh; see help(pcolormesh)' + X, Y, C = self._pcolorargs('pcolormesh', *args) + Ny, Nx = X.shape - Nx = X.shape[-1] - Ny = Y.shape[0] - if len(X.shape) <> 2 or X.shape[0] == 1: - X = X.ravel().resize((Ny, Nx)) - if len(Y.shape) <> 2 or Y.shape[1] == 1: - Y = Y.ravel().resize((Nx, Ny)).T - # convert to one dimensional arrays C = ma.ravel(C[0:Ny-1, 0:Nx-1]) # data point in each cell is value at lower left corner X = X.ravel() Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-08 18:10:11 UTC (rev 3928) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-08 18:15:05 UTC (rev 3929) @@ -387,5 +387,5 @@ def print_png(self, filename, *args, **kwargs): self.draw() - self.get_renderer()._renderer.write_png(str(filename)) + self.get_renderer()._renderer.write_png(str(filename), self.figure.dpi.get()) Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-10-08 18:10:11 UTC (rev 3928) +++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-10-08 18:15:05 UTC (rev 3929) @@ -97,6 +97,8 @@ 0, 0, 0, 0, w, h) if DEBUG: print 'FigureCanvasGTKAgg.done' + def print_png(self, filename, *args, **kwargs): + return FigureCanvasAgg.print_png(self, filename, *args, **kwargs) """\ Traceback (most recent call last): Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-08 18:10:11 UTC (rev 3928) +++ branches/transforms/src/_backend_agg.cpp 2007-10-08 18:15:05 UTC (rev 3929) @@ -1072,7 +1072,7 @@ { _VERBOSE("RendererAgg::write_png"); - args.verify_length(1); + args.verify_length(1, 2); FILE *fp; Py::Object o = Py::Object(args[0]); @@ -1133,6 +1133,13 @@ width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + // Save the dpi of the image in the file + if (args.size() == 2) { + double dpi = Py::Float(args[1]); + size_t dots_per_meter = (size_t)(dpi / (2.54 / 100.0)); + png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); + } // this a a color image! sig_bit.gray = 0; @@ -1718,7 +1725,7 @@ add_varargs_method("write_rgba", &RendererAgg::write_rgba, "write_rgba(fname)"); add_varargs_method("write_png", &RendererAgg::write_png, - "write_png(fname)"); + "write_png(fname, dpi=None)"); add_varargs_method("tostring_rgb", &RendererAgg::tostring_rgb, "s = tostring_rgb()"); add_varargs_method("tostring_argb", &RendererAgg::tostring_argb, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-08 18:10:13
|
Revision: 3928 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3928&view=rev Author: mdboom Date: 2007-10-08 11:10:11 -0700 (Mon, 08 Oct 2007) Log Message: ----------- More work on collections. Modified Paths: -------------- branches/transforms/examples/collections_demo.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/setupext.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/examples/collections_demo.py =================================================================== --- branches/transforms/examples/collections_demo.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/examples/collections_demo.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -45,6 +45,8 @@ a = fig.add_subplot(2,2,1) col = collections.LineCollection([spiral], offsets=xyo, transOffset=a.transData) +trans = transforms.Affine2D().scale(fig.dpi/72.0) +col.set_transform(trans) # the points to pixels transform # Note: the first argument to the collection initializer # must be a list of sequences of x,y tuples; we have only # one sequence, but we still have to put it in a list. @@ -59,9 +61,6 @@ # Make a transform for the line segments such that their size is # given in points: -trans = transforms.scale_transform(fig.dpi/transforms.Value(72.), - fig.dpi/transforms.Value(72.)) -col.set_transform(trans) # the points to pixels transform col.set_color(colors) a.autoscale_view() # See comment above, after a.add_collection. @@ -74,28 +73,25 @@ col = collections.PolyCollection([spiral], offsets=xyo, transOffset=a.transData) -a.add_collection(col, autolim=True) -trans = transforms.scale_transform(fig.dpi/transforms.Value(72.), - fig.dpi/transforms.Value(72.)) +trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform +a.add_collection(col, autolim=True) col.set_color(colors) a.autoscale_view() a.set_title('PolyCollection using offsets') - # 7-sided regular polygons a = fig.add_subplot(2,2,3) col = collections.RegularPolyCollection(fig.dpi, 7, - sizes = N.fabs(xx)*10, offsets=xyo, + sizes = N.fabs(xx) / 10.0, offsets=xyo, transOffset=a.transData) -a.add_collection(col, autolim=True) -trans = transforms.scale_transform(fig.dpi/transforms.Value(72.), - fig.dpi/transforms.Value(72.)) +trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform +a.add_collection(col, autolim=True) col.set_color(colors) a.autoscale_view() a.set_title('RegularPolyCollection using offsets') Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -1046,7 +1046,7 @@ self._set_artist_props(collection) collection.set_clip_path(self.axesPatch) if autolim: - self.update_datalim(collection.get_verts(self.transData)) + self.update_datalim(collection.get_datalim(self.transData)) collection._remove_method = lambda h: self.collections.remove(h) def add_line(self, line): @@ -1105,6 +1105,9 @@ # limits and set the bound to be the bounds of the xydata. # Otherwise, it will compute the bounds of it's current data # and the data in xydata + # MGDTODO: This isn't always the most efficient way to do this... in + # some cases things should use update_datalim_bounds + if not ma.isMaskedArray(xys): xys = npy.asarray(xys) self.update_datalim_numerix(xys[:, 0], xys[:, 1]) @@ -1119,6 +1122,10 @@ self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits) self.ignore_existing_data_limits = False + def update_datalim_bounds(self, bounds): + # MGDTODO: Document me + self.dataLim.bounds = Bbox.union([self.dataLim, bounds]).bounds + def _get_verts_in_data_coords(self, trans, xys): if trans == self.transData: return xys @@ -4006,8 +4013,8 @@ shading='faceted --> edgecolors=None edgecolors also can be any mpl color or sequence of colors. - Optional kwargs control the PatchCollection properties: - %(PatchCollection)s + Optional kwargs control the Collection properties: + %(Collection)s """ if not self._hold: self.cla() @@ -4439,7 +4446,7 @@ * alpha=1.0 : the alpha blending value - Return value is a mcoll.PatchCollection + Return value is a mcoll.Collection object Grid Orientation @@ -4627,7 +4634,7 @@ * alpha=1.0 : the alpha blending value - Return value is a collections.PatchCollection + Return value is a collections.Collection object See pcolor for an explantion of the grid orientation and the Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -131,11 +131,15 @@ self._renderer.draw_markers(gc, marker_path, marker_trans.frozen(), path, trans.frozen(), rgbFace) def draw_path_collection(self, master_transform, clipbox, clippath, clippath_trans, - paths, transforms, facecolors, edgecolors, linewidths, linestyles, antialiaseds): + paths, transforms, offsets, transOffset, facecolors, edgecolors, + linewidths, linestyles, antialiaseds): assert master_transform.is_affine + if transOffset is not None: + transOffset = transOffset.frozen() self._renderer.draw_path_collection( master_transform.frozen(), clipbox, clippath, clippath_trans, - paths, transforms, facecolors, edgecolors, linewidths, linestyles, antialiaseds) + paths, transforms, offsets, transOffset, facecolors, edgecolors, linewidths, + linestyles, antialiaseds) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -19,67 +19,20 @@ import matplotlib.nxutils as nxutils import matplotlib.path as path -# MGDTODO: Now that everything draws through draw_path_collection, -# perhaps more stuff could be moved into the Collection base class -# here +# MGDTODO: Move this stuff +from matplotlib.backends._backend_agg import get_path_collection_extents, \ + point_in_path_collection -class Collection(artist.Artist): +class Collection(artist.Artist, cm.ScalarMappable): """ + Base class for Collections. Must be subclassed to be usable. + All properties in a collection must be sequences or scalars; if scalars, they will be converted to sequences. The property of the ith element of the collection is the prop[i % len(props)]. - """ - - def __init__(self): - artist.Artist.__init__(self) - - - def get_verts(self): - 'return seq of (x,y) in collection' - raise NotImplementedError('Derived must override') - - def _get_value(self, val): - try: return (float(val), ) - except TypeError: - if cbook.iterable(val) and len(val): - try: float(val[0]) - except TypeError: pass # raise below - else: return val - - raise TypeError('val must be a float or nonzero sequence of floats') - - -# these are not available for the object inspector until after the -# class is built so we define an initial set here for the init -# function and they will be overridden after object defn -artist.kwdocd['PatchCollection'] = """\ - Valid PatchCollection kwargs are: - - edgecolors=None, - facecolors=None, - linewidths=None, - antialiaseds = None, - offsets = None, - transOffset = transforms.identity_transform(), - norm = None, # optional for cm.ScalarMappable - cmap = None, # ditto - - offsets and transOffset are used to translate the patch after - rendering (default no offsets) - - If any of edgecolors, facecolors, linewidths, antialiaseds are - None, they default to their patch.* rc params setting, in sequence - form. -""" - -class PatchCollection(Collection, cm.ScalarMappable): - """ - Base class for filled regions such as PolyCollection etc. - It must be subclassed to be usable. - kwargs are: edgecolors=None, @@ -103,30 +56,45 @@ draw time a call to scalar mappable will be made to set the face colors. """ + _offsets = npy.zeros((1, 2)) + _transOffset = transforms.IdentityTransform() + + _facecolors = [None] + _edgecolors = [None] + _lw = [1.0] + _ls = [None] + _aa = [True] + _pickradius = 5.0 + _transforms = [None] + zorder = 1 def __init__(self, edgecolors=None, facecolors=None, linewidths=None, + linestyles='solid', antialiaseds = None, offsets = None, transOffset = None, norm = None, # optional for ScalarMappable cmap = None, # ditto + pickradius = 5.0, + **kwargs ): """ Create a PatchCollection %(PatchCollection)s """ - Collection.__init__(self) + artist.Artist.__init__(self) cm.ScalarMappable.__init__(self, norm, cmap) if facecolors is None: facecolors = mpl.rcParams['patch.facecolor'] if edgecolors is None: edgecolors = mpl.rcParams['patch.edgecolor'] if linewidths is None: linewidths = (mpl.rcParams['patch.linewidth'],) if antialiaseds is None: antialiaseds = (mpl.rcParams['patch.antialiased'],) - + self.set_linestyles(linestyles) + self._facecolors = _colors.colorConverter.to_rgba_list(facecolors) if edgecolors == 'None': self._edgecolors = self._facecolors @@ -135,13 +103,80 @@ self._edgecolors = _colors.colorConverter.to_rgba_list(edgecolors) self._linewidths = self._get_value(linewidths) self._antialiaseds = self._get_value(antialiaseds) - #self._offsets = offsets - self._offsets = offsets - self._transOffset = transOffset - self._verts = [] - __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + self._uniform_offsets = None + self._offsets = npy.zeros((1, 2)) + if offsets is not None: + offsets = npy.asarray(offsets) + if len(offsets.shape) == 1: + offsets = offsets[npy.newaxis,:] # Make it Nx2. + if transOffset is not None: + Affine2D = transforms.Affine2D + self._offsets = offsets + self._transOffset = transOffset + else: + self._uniform_offsets = offsets + self._pickradius = pickradius + self.update(kwargs) + + def _get_value(self, val): + try: return (float(val), ) + except TypeError: + if cbook.iterable(val) and len(val): + try: float(val[0]) + except TypeError: pass # raise below + else: return val + + raise TypeError('val must be a float or nonzero sequence of floats') + + def get_paths(self): + raise NotImplementedError + + def get_transforms(self): + return self._transforms + + def get_datalim(self, transData): + result = transforms.Bbox.from_lbrt(*get_path_collection_extents( + self.get_transform().frozen(), + self.get_paths(), + self.get_transforms(), + self._offsets, + self._transOffset.frozen())) + result = result.transformed(transData.inverted()) + return result + + def draw(self, renderer): + if not self.get_visible(): return + renderer.open_group(self.__class__.__name__) + transform = self.get_transform() + transOffset = self._transOffset + offsets = self._offsets + + # MGDTODO: Transform the paths (since we don't keep track of segments anymore + if self.have_units(): + segments = [] + for segment in self._segments: + xs, ys = zip(*segment) + xs = self.convert_xunits(xs) + ys = self.convert_yunits(ys) + segments.append(zip(xs, ys)) +# if self._offsets is not None: +# xs = self.convert_xunits(self._offsets[:0]) +# ys = self.convert_yunits(self._offsets[:1]) +# offsets = zip(xs, ys) + + self.update_scalarmappable() + + #print 'calling renderer draw line collection' + clippath, clippath_trans = self.get_transformed_clip_path_and_affine() + + renderer.draw_path_collection( + transform, self.clipbox, clippath, clippath_trans, + self.get_paths(), self.get_transforms(), offsets, transOffset, + self._facecolors, self._edgecolors, self._lw, self._ls, self._aa) + renderer.close_group(self.__class__.__name__) + def contains(self, mouseevent): """ Test whether the mouse event occurred in the collection. @@ -149,25 +184,13 @@ Returns T/F, dict(ind=itemlist), where every item in itemlist contains the event. """ if callable(self._contains): return self._contains(self,mouseevent) - # TODO: Consider doing the test in data coordinates - # Patch transforms the mouse into data coordinates and does the - # test for membership there. This is more efficient though it - # may not match the visual appearance of the polygon on the - # screen. Regardless, patch and patch collection should use - # the same algorithm. Here's the code in patch: - # - # x, y = self.get_transform().inverse_xy_tup((mouseevent.x, mouseevent.y)) - # xyverts = self.get_verts() - # inside = nxutils.pnpoly(x, y, xyverts) - # - ind = [] - x, y = mouseevent.x, mouseevent.y - for i, thispoly in enumerate(self.get_transformed_patches()): - inside = nxutils.pnpoly(x, y, thispoly) - if inside: ind.append(i) + ind = point_in_path_collection( + mouseevent.x, mouseevent.y, self._pickradius, + self.get_transform(), self._paths, self._transforms, self._offsets, + self._offsetTrans, self._facecolors) return len(ind)>0,dict(ind=ind) - + # MGDTODO def get_transformed_patches(self): """ get a sequence of the polygons in the collection in display (transformed) space @@ -206,12 +229,9 @@ data.append(tverts) return data - def get_transoffset(self): - if self._transOffset is None: - self._transOffset = transforms.identity_transform() - return self._transOffset - - + def set_pickradius(self,pickradius): self.pickradius = 5 + def get_pickradius(self): return self.pickradius + def set_linewidth(self, lw): """ Set the linewidth(s) for the collection. lw can be a scalar or a @@ -224,6 +244,36 @@ def set_linewidths(self, lw): self.set_linewidth(lw) + def set_linestyles(self, ls): + """ + Set the linestyles(s) for the collection. + ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) ] + """ + try: + if cbook.is_string_like(ls): + dashes = [backend_bases.GraphicsContextBase.dashd[ls]] + elif cbook.iterable(ls): + try: + dashes = [] + for x in ls: + if cbook.is_string_like(x): + dashes.append(backend_bases.GraphicsContextBase.dashd[ls]) + elif cbook.iterator(x) and len(x) == 2: + dashes.append(x) + else: + raise ValueError() + except ValueError: + if len(ls)==2: + dashes = ls + else: + raise ValueError() + else: + raise ValueError() + except ValueError: + raise ValueError('Do not know how to convert %s to dashes'%ls) + + self._ls = dashes + def set_color(self, c): """ Set both the edgecolor and the facecolor. @@ -277,6 +327,15 @@ if cbook.is_string_like(self._edgecolors) and self._edgecolors != 'None': self._edgecolors = [(r,g,b,alpha) for r,g,b,a in self._edgecolors] + def get_linewidth(self): + return self._lw + + def get_linestyle(self): + return self._ls + + def get_dashes(self): + return self._ls + def update_scalarmappable(self): """ If the scalar mappable array is not none, update facecolors @@ -289,7 +348,31 @@ self._facecolors = self.to_rgba(self._A, self._alpha) #print self._facecolors -class QuadMesh(PatchCollection): + +# these are not available for the object inspector until after the +# class is built so we define an initial set here for the init +# function and they will be overridden after object defn +artist.kwdocd['Collection'] = """\ + Valid Collection kwargs are: + + edgecolors=None, + facecolors=None, + linewidths=None, + antialiaseds = None, + offsets = None, + transOffset = transforms.identity_transform(), + norm = None, # optional for cm.ScalarMappable + cmap = None, # ditto + + offsets and transOffset are used to translate the patch after + rendering (default no offsets) + + If any of edgecolors, facecolors, linewidths, antialiaseds are + None, they default to their patch.* rc params setting, in sequence + form. +""" + +class QuadMesh(Collection): """ Class for the efficient drawing of a quadrilateral mesh. A quadrilateral mesh consists of a grid of vertices. The dimensions @@ -315,7 +398,7 @@ (0, 2) .. (0, meshWidth), (1, 0), (1, 1), and so on. """ def __init__(self, meshWidth, meshHeight, coordinates, showedges): - PatchCollection.__init__(self) + Collection.__init__(self) self._meshWidth = meshWidth self._meshHeight = meshHeight self._coordinates = coordinates @@ -329,7 +412,7 @@ # when creating/changing ****** Why not? speed? if not self.get_visible(): return transform = self.get_transform() - transoffset = self.get_transoffset() + transoffset = self._transOffset transform.freeze() transoffset.freeze() #print 'QuadMesh draw' @@ -342,63 +425,26 @@ transform.thaw() transoffset.thaw() -class PolyCollection(PatchCollection): +class PolyCollection(Collection): def __init__(self, verts, **kwargs): """ verts is a sequence of ( verts0, verts1, ...) where verts_i is a sequence of xy tuples of vertices, or an equivalent numpy array of shape (nv,2). - %(PatchCollection)s + %(Collection)s """ - PatchCollection.__init__(self,**kwargs) - self._verts = verts + Collection.__init__(self,**kwargs) + self.set_verts(verts) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def set_verts(self, verts): '''This allows one to delay initialization of the vertices.''' - self._verts = verts + self._paths = [path.Path(v, closed=True) for v in verts] - def draw(self, renderer): - if not self.get_visible(): return - renderer.open_group('polycollection') - transform = self.get_transform() - transoffset = self.get_transoffset() - - - transform.freeze() - transoffset.freeze() - self.update_scalarmappable() - if cbook.is_string_like(self._edgecolors) and self._edgecolors[:2] == 'No': - self._linewidths = (0,) - #self._edgecolors = self._facecolors - renderer.draw_poly_collection( - self._verts, transform, self.clipbox, - self._facecolors, self._edgecolors, - self._linewidths, self._antialiaseds, - self._offsets, transoffset) - transform.thaw() - transoffset.thaw() - renderer.close_group('polycollection') - - - def get_verts(self, dataTrans=None): - '''Return vertices in data coordinates. - The calculation is incomplete in general; it is based - on the vertices or the offsets, whichever is using - dataTrans as its transformation, so it does not take - into account the combined effect of segments and offsets. - ''' - verts = [] - if self._offsets is None: - for seg in self._verts: - verts.extend(seg) - return [tuple(xy) for xy in verts] - if self.get_transoffset() == dataTrans: - return [tuple(xy) for xy in self._offsets] - raise NotImplementedError('Vertices in data coordinates are calculated\n' - + 'with offsets only if _transOffset == dataTrans.') - + def get_paths(self): + return self._paths + class BrokenBarHCollection(PolyCollection): """ A colleciton of horizontal bars spanning yrange with a sequence of @@ -409,7 +455,7 @@ xranges : sequence of (xmin, xwidth) yrange : ymin, ywidth - %(PatchCollection)s + %(Collection)s """ ymin, ywidth = yrange ymax = ymin + ywidth @@ -417,7 +463,7 @@ PolyCollection.__init__(self, verts, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd -class RegularPolyCollection(PatchCollection): +class RegularPolyCollection(Collection): def __init__(self, dpi, numsides, @@ -437,7 +483,7 @@ * rotation is the rotation of the polygon in radians - %(PatchCollection)s + %(Collection)s Example: see examples/dynamic_collection.py for complete example @@ -459,14 +505,18 @@ """ - PatchCollection.__init__(self,**kwargs) + Collection.__init__(self,**kwargs) self._sizes = sizes self._dpi = dpi - self.numsides = numsides - self.rotation = rotation - self._update_verts() + self._paths = [path.Path.unit_regular_polygon(numsides)] + self._transforms = [transforms.Affine2D().scale(x) for x in sizes] + __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + def get_paths(self): + return self._paths + + # MGDTODO def get_transformed_patches(self): # Shouldn't need all these calls to asarray; # the variables should be converted when stored. @@ -485,62 +535,6 @@ return polys - def _update_verts(self): - r = 1.0/math.sqrt(math.pi) # unit area - theta = (2*math.pi/self.numsides)*npy.arange(self.numsides) + self.rotation - self._verts = zip( r*npy.sin(theta), r*npy.cos(theta) ) - - def draw(self, renderer): - if not self.get_visible(): return - renderer.open_group('regpolycollection') - transform = self.get_transform() - transoffset = self.get_transoffset() - - transform.freeze() - transoffset.freeze() - self.update_scalarmappable() - self._update_verts() - scales = npy.sqrt(npy.asarray(self._sizes)*self._dpi.get()/72.0) - - - offsets = self._offsets - if self._offsets is not None: - xs, ys = zip(*offsets) - #print 'converting: units=%s, converter=%s'%(self.axes.xaxis.units, self.axes.xaxis.converter) - xs = self.convert_xunits(xs) - ys = self.convert_yunits(ys) - offsets = zip(xs, ys) - else: - offsets = None - - #print 'drawing offsets', offsets - #print 'drawing verts', self._verts - #print 'drawing scales', scales - if cbook.is_string_like(self._edgecolors) and self._edgecolors[:2] == 'No': - #self._edgecolors = self._facecolors - self._linewidths = (0,) - renderer.draw_regpoly_collection( - self.clipbox, - offsets, transoffset, - self._verts, scales, - self._facecolors, self._edgecolors, - self._linewidths, self._antialiaseds) - - transform.thaw() - transoffset.thaw() - renderer.close_group('regpolycollection') - - - def get_verts(self, dataTrans=None): - '''Return vertices in data coordinates. - The calculation is incomplete; it uses only - the offsets, and only if _transOffset is dataTrans. - ''' - if self.get_transoffset() == dataTrans: - return [tuple(xy) for xy in self._offsets] - raise NotImplementedError('Vertices in data coordinates are calculated\n' - + 'only with offsets and only if _transOffset == dataTrans.') - class StarPolygonCollection(RegularPolyCollection): def __init__(self, dpi, @@ -561,7 +555,7 @@ * rotation is the rotation of the polygon in radians - %(PatchCollection)s + %(Collection)s """ RegularPolyCollection.__init__(self, dpi, numsides, rotation, sizes, **kwargs) @@ -595,7 +589,7 @@ * rotation is the rotation of the polygon in radians - %(PatchCollection)s + %(Collection)s """ RegularPolyCollection.__init__(self, dpi, numsides, rotation, sizes, **kwargs) @@ -621,9 +615,8 @@ colors = None, antialiaseds = None, linestyles = 'solid', - # MGDTODO: Deal with offsets (mayb -# offsets = None, -# transOffset = None,#transforms.identity_transform(), + offsets = None, + transOffset = None, norm = None, cmap = None, pickradius = 5, @@ -668,168 +661,52 @@ matrix _A is not None (ie a call to set_array has been made), at draw time a call to scalar mappable will be made to set the colors. """ - Collection.__init__(self) - cm.ScalarMappable.__init__(self, norm, cmap) + if colors is None: colors = mpl.rcParams['lines.color'] + if linewidths is None: linewidths = (mpl.rcParams['lines.linewidth'],) + if antialiaseds is None: antialiaseds = (mpl.rcParams['lines.antialiased'],) + self.set_linestyles(linestyles) - if linewidths is None : - linewidths = (mpl.rcParams['lines.linewidth'], ) - if colors is None : - colors = (mpl.rcParams['lines.color'],) - if antialiaseds is None : - antialiaseds = (mpl.rcParams['lines.antialiased'], ) + Collection.__init__( + self, + edgecolors=colors, + linewidths=linewidths, + linestyles=linestyles, + antialiaseds=antialiaseds, + offsets=offsets, + transOffset=transOffset, + norm=norm, + cmap=cmap, + pickradius=pickradius, + **kwargs) - self._colors = _colors.colorConverter.to_rgba_list(colors) - self._aa = self._get_value(antialiaseds) - self._lw = self._get_value(linewidths) - self.set_linestyles(linestyles) - self._uniform_offsets = None - # MGDTODO: Deal with offsets -# if offsets is not None: -# offsets = npy.asarray(offsets) -# if len(offsets.shape) == 1: -# offsets = offsets[npy.newaxis,:] # Make it Nx2. -# if transOffset is None: -# if offsets is not None: -# self._uniform_offsets = offsets -# offsets = None -# transOffset = transforms.IdentityTransform() -# self._offsets = offsets -# self._transOffset = transOffset + self._facecolors = [None] self.set_segments(segments) - self.pickradius = pickradius - self.update(kwargs) - def contains(self, mouseevent): - """ - Test whether the mouse event occurred in the collection. - - Returns T/F, dict(ind=itemlist), where every item in itemlist contains the event. - """ - import matplotlib.lines as ML - if callable(self._contains): return self._contains(self,mouseevent) - - # TODO: add offset processing; adjusting the mouse for each offset - # will be somewhat cheaper than adjusting the segments. - if self._offsets != None: - raise NotImplementedError, "LineCollection does not yet support picking with offsets" - - mx,my = mouseevent.x,mouseevent.y - transform = self.get_transform() - - # MGDTODO: Numpify - ind = [] - for this in xrange(len(self._segments)): - xy = transform.transform_point(self._segments[this]) - this_ind = ML.segment_hits(mx,my,xy[:,0],xy[:,1],self.pickradius) - ind.extend([(this,k) for k in this_ind]) - return len(ind)>0,dict(ind=ind) - - def set_pickradius(self,pickradius): self.pickradius = 5 - def get_pickradius(self): return self.pickradius - - # MGDTODO: Support offsets (maybe) -# def get_transoffset(self): -# if self._transOffset is None: -# self._transOffset = transforms.identity_transform() -# return self._transOffset - + def get_paths(self): + return self._paths + def set_segments(self, segments): if segments is None: return + segments = [npy.asarray(seg, npy.float_) for seg in segments] + if self._uniform_offsets is not None: + segments = self._add_offsets(segments) self._paths = [path.Path(seg, closed=False) for seg in segments] -# if self._uniform_offsets is not None: -# self._add_offsets() - set_verts = set_segments # for compatibility with PolyCollection - # MGDTODO: Support offsets (maybe) -# def _add_offsets(self): -# segs = self._segments -# offsets = self._uniform_offsets -# Nsegs = len(segs) -# Noffs = offsets.shape[0] -# if Noffs == 1: -# for i in range(Nsegs): -# segs[i] = segs[i] + i * offsets -# else: -# for i in range(Nsegs): -# io = i%Noffs -# segs[i] = segs[i] + offsets[io:io+1] + def _add_offsets(self, segs): + offsets = self._uniform_offsets + Nsegs = len(segs) + Noffs = offsets.shape[0] + if Noffs == 1: + for i in range(Nsegs): + segs[i] = segs[i] + i * offsets + else: + for i in range(Nsegs): + io = i%Noffs + segs[i] = segs[i] + offsets[io:io+1] + return segs - def draw(self, renderer): - if not self.get_visible(): return - renderer.open_group('linecollection') - transform = self.get_transform() - # MGDTODO: Deal with offsets (maybe) -# transoffset = self.get_transoffset() - - # segments = self._segments - # MGDTODO: Deal with offsets (maybe) - # offsets = self._offsets - - # MGDTODO: Transform the paths (since we don't keep track of segments anymore - if self.have_units(): - segments = [] - for segment in self._segments: - xs, ys = zip(*segment) - xs = self.convert_xunits(xs) - ys = self.convert_yunits(ys) - segments.append(zip(xs, ys)) -# if self._offsets is not None: -# xs = self.convert_xunits(self._offsets[:0]) -# ys = self.convert_yunits(self._offsets[:1]) -# offsets = zip(xs, ys) - - self.update_scalarmappable() - #print 'calling renderer draw line collection' - clippath, clippath_trans = self.get_transformed_clip_path_and_affine() - renderer.draw_path_collection( - transform, self.clipbox, clippath, clippath_trans, - self._paths, [None], [None], - self._colors, self._lw, self._ls, self._aa) - renderer.close_group('linecollection') - - def set_linewidth(self, lw): - """ - Set the linewidth(s) for the collection. lw can be a scalar or a - sequence; if it is a sequence the patches will cycle through the - sequence - - ACCEPTS: float or sequence of floats - """ - - self._lw = self._get_value(lw) - - def set_linestyles(self, ls): - """ - Set the linestyles(s) for the collection. - ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) ] - """ - try: - if cbook.is_string_like(ls): - dashes = [backend_bases.GraphicsContextBase.dashd[ls]] - elif cbook.iterable(ls): - try: - dashes = [] - for x in ls: - if cbook.is_string_like(x): - dashes.append(backend_bases.GraphicsContextBase.dashd[ls]) - elif cbook.iterator(x) and len(x) == 2: - dashes.append(x) - else: - raise ValueError() - except ValueError: - if len(ls)==2: - dashes = ls - else: - raise ValueError() - else: - raise ValueError() - except ValueError: - raise ValueError('Do not know how to convert %s to dashes'%ls) - - self._ls = dashes - def set_color(self, c): """ Set the color(s) of the line collection. c can be a @@ -839,7 +716,7 @@ ACCEPTS: matplotlib color arg or sequence of rgba tuples """ - self._colors = _colors.colorConverter.to_rgba_list(c) + self._edgecolors = _colors.colorConverter.to_rgba_list(c) def color(self, c): """ @@ -853,48 +730,12 @@ warnings.warn('LineCollection.color deprecated; use set_color instead') return self.set_color(c) - def set_alpha(self, alpha): - """ - Set the alpha tranpancies of the collection. Alpha can be a - float, in which case it is applied to the entire collection, - or a sequence of floats - - ACCEPTS: float or sequence of floats - """ - - try: float(alpha) - except TypeError: raise TypeError('alpha must be a float') - else: - artist.Artist.set_alpha(self, alpha) - self._colors = [(r,g,b,alpha) for r,g,b,a in self._colors] - - def get_linewidth(self): - return self._lw - - def get_linestyle(self): - return self._ls - - def get_dashes(self): - return self._ls - def get_color(self): - return self._colors + return self._edgecolors get_colors = get_color # for compatibility with old versions - def update_scalarmappable(self): - """ - If the scalar mappable array is not none, update colors - from scalar data - """ - if self._A is None: return - if len(self._A.shape)>1: - raise ValueError('LineCollections can only map rank 1 arrays') - self._colors = self.to_rgba(self._A, self._alpha) - - -artist.kwdocd['Collection'] = artist.kwdoc(Collection) -artist.kwdocd['PatchCollection'] = patchstr = artist.kwdoc(PatchCollection) +artist.kwdocd['Collection'] = patchstr = artist.kwdoc(Collection) for k in ('QuadMesh', 'PolyCollection', 'BrokenBarHCollection', 'RegularPolyCollection', 'StarPolygonCollection'): artist.kwdocd[k] = patchstr Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/legend.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -32,7 +32,7 @@ from lines import Line2D from mlab import segments_intersect from patches import Patch, Rectangle, RegularPolygon, Shadow, bbox_artist, draw_bbox -from collections import LineCollection, RegularPolyCollection, PatchCollection +from collections import LineCollection, RegularPolyCollection from text import Text from transforms import Affine2D, Bbox, BboxTransform Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -349,24 +349,14 @@ self._picker = p def get_window_extent(self, renderer): - # MGDTODO: Numpify - xy = self.get_transform().transform(self._xy) + bbox = Bbox() + bbox.update_from_data(self.get_transform().transform(self._xy)) - x = xy[:, 0] - y = xy[:, 1] - left = x.min() - bottom = y.min() - width = x.max() - left - height = y.max() - bottom - # correct for marker size, if any if self._marker is not None: ms = self._markersize / 72.0 * self.figure.dpi - left -= ms/2 - bottom -= ms/2 - width += ms - height += ms - return Bbox.from_lbwh(left, bottom, width, height) + bbox = Bbox(bbox.get_points() + [[-ms/2.0, ms/2.0]]) + return bbox def set_axes(self, ax): Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -395,6 +395,8 @@ when False, include the existing bounds of the Bbox. when None, use the last value passed to Bbox.ignore(). """ + # MGDTODO: It may be more efficient for some callers to use update_from_data_xy instead + if ignore is None: ignore = self._ignore @@ -430,6 +432,18 @@ npy.float_) self._minpos = npy.minimum(minpos, self._minpos) self.invalidate() + + def update_from_data_xy(self, xy, ignore=None): + """ + Update the bounds of the Bbox based on the passed in data. + + xy: a numpy array of 2D points + ignore: + when True, ignore the existing bounds of the Bbox. + when False, include the existing bounds of the Bbox. + when None, use the last value passed to Bbox.ignore(). + """ + return self.update_from_data(xy[:, 0], xy[:, 1], ignore) def _set_xmin(self, val): self._points[0, 0] = val Modified: branches/transforms/setupext.py =================================================================== --- branches/transforms/setupext.py 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/setupext.py 2007-10-08 18:10:11 UTC (rev 3928) @@ -352,6 +352,7 @@ try_pkgconfig(module, 'libpng', 'png') module.libraries.append('z') add_base_flags(module) + add_numpy_flags(module) module.include_dirs.extend(['src','swig', '%s/include'%AGG_VERSION, '.']) # put these later for correct link order @@ -823,7 +824,7 @@ # add agg flags before pygtk because agg only supports freetype1 # and pygtk includes freetype2. This is a bit fragile. - + add_tk_flags(module) # do this first add_agg_flags(module) add_ft2font_flags(module) Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/src/_backend_agg.cpp 2007-10-08 18:10:11 UTC (rev 3928) @@ -336,9 +336,11 @@ if (bbox_obj.ptr() != Py_None) { PyArrayObject* bbox = (PyArrayObject*) PyArray_FromObject(bbox_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) + if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) { + Py_XDECREF(bbox); throw Py::TypeError ("Expected a Bbox object."); + } *l = *(double*)PyArray_GETPTR2(bbox, 0, 0); double _b = *(double*)PyArray_GETPTR2(bbox, 0, 1); @@ -346,8 +348,11 @@ double _t = *(double*)PyArray_GETPTR2(bbox, 1, 1); *b = height - _t; *t = height - _b; + + Py_XDECREF(bbox); return true; } + return false; } @@ -920,7 +925,7 @@ Py::Object RendererAgg::draw_path_collection(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_path_collection"); - args.verify_length(11); + args.verify_length(13); //segments, trans, clipbox, colors, linewidths, antialiaseds agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[0]); @@ -929,23 +934,30 @@ agg::trans_affine clippath_trans = py_to_agg_transformation_matrix(args[3], false); Py::SeqBase<Py::Object> paths = args[4]; Py::SeqBase<Py::Object> transforms_obj = args[5]; - Py::SeqBase<Py::Object> facecolors_obj = args[6]; - Py::SeqBase<Py::Object> edgecolors_obj = args[7]; - Py::SeqBase<Py::Float> linewidths = args[8]; - Py::SeqBase<Py::Object> linestyles_obj = args[9]; - Py::SeqBase<Py::Int> antialiaseds = args[10]; + Py::Object offsets_obj = args[6]; + agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7], false); + Py::SeqBase<Py::Object> facecolors_obj = args[8]; + Py::SeqBase<Py::Object> edgecolors_obj = args[9]; + Py::SeqBase<Py::Float> linewidths = args[10]; + Py::SeqBase<Py::Object> linestyles_obj = args[11]; + Py::SeqBase<Py::Int> antialiaseds = args[12]; GCAgg gc(dpi, false); + PyArrayObject* offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!offsets || offsets->dimensions[1] != 2) + throw Py::ValueError("Offsets array must be Nx2"); + size_t Npaths = paths.length(); - size_t Ntransforms = std::min(transforms_obj.length(), Npaths); - size_t Nfacecolors = std::min(facecolors_obj.length(), Npaths); - size_t Nedgecolors = std::min(edgecolors_obj.length(), Npaths); + size_t Noffsets = offsets->dimensions[0]; + size_t N = std::max(Npaths, Noffsets); + size_t Ntransforms = std::min(transforms_obj.length(), N); + size_t Nfacecolors = std::min(facecolors_obj.length(), N); + size_t Nedgecolors = std::min(edgecolors_obj.length(), N); size_t Nlinewidths = linewidths.length(); - size_t Nlinestyles = std::min(linestyles_obj.length(), Npaths); + size_t Nlinestyles = std::min(linestyles_obj.length(), N); size_t Naa = antialiaseds.length(); - size_t N = Npaths; size_t i = 0; // Convert all of the transforms up front @@ -983,7 +995,7 @@ } } - // Convert all of the edges up front + // Convert all of the edgecolors up front typedef std::vector<agg::rgba> edgecolors_t; edgecolors_t edgecolors; edgecolors.reserve(Nedgecolors); @@ -1016,18 +1028,23 @@ bool has_clippath = render_clippath(clippath, clippath_trans); for (i = 0; i < N; ++i) { - facepair_t& face = facecolors[i % Nfacecolors]; - gc.color = edgecolors[i % Nedgecolors]; - gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; - gc.dashes = dashes[i % Nlinestyles].second; - gc.dashOffset = dashes[i % Nlinestyles].first; - gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); + double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); + double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); + offset_trans.transform(&xo, &yo); + agg::trans_affine_translation transOffset(xo, yo); agg::trans_affine& trans = transforms[i % Ntransforms]; - PathIterator path(paths[i]); + facepair_t& face = facecolors[i % Nfacecolors]; + gc.color = edgecolors[i % Nedgecolors]; + gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; + gc.dashes = dashes[i % Nlinestyles].second; + gc.dashOffset = dashes[i % Nlinestyles].first; + gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); + PathIterator path(paths[i % Npaths]); bool snap = (path.total_vertices() == 2); - _draw_path(path, trans, snap, has_clippath, face, gc); + _draw_path(path, trans * transOffset, snap, has_clippath, face, gc); } + Py_XDECREF(offsets); return Py::Object(); } @@ -1482,11 +1499,9 @@ void get_path_extents(PathIterator& path, agg::trans_affine& trans, double* x0, double* y0, double* x1, double* y1) { - typedef agg::conv_transform<PathIterator> transformed_path_t; - typedef agg::conv_curve<transformed_path_t> curve_t; + typedef agg::conv_curve<PathIterator> curve_t; - transformed_path_t trans_path(path, trans); - curve_t curved_path(trans_path); + curve_t curved_path(path); double x, y; curved_path.rewind(0); @@ -1505,6 +1520,9 @@ if (x > *x1) *x1 = x; if (y > *y1) *y1 = y; } + + trans.transform(x0, y0); + trans.transform(x1, y1); } Py::Object _backend_agg_module::get_path_extents(const Py::Tuple& args) { @@ -1524,6 +1542,133 @@ return result; } +struct PathCollectionExtents { + double x0, y0, x1, y1; +}; + +Py::Object _backend_agg_module::get_path_collection_extents(const Py::Tuple& args) { + args.verify_length(5); + + //segments, trans, clipbox, colors, linewidths, antialiaseds + agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[0]); + Py::SeqBase<Py::Object> paths = args[1]; + Py::SeqBase<Py::Object> transforms_obj = args[2]; + Py::SeqBase<Py::Object> offsets = args[3]; + agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[4], false); + + size_t Npaths = paths.length(); + size_t Noffsets = offsets.length(); + size_t N = std::max(Npaths, Noffsets); + size_t Ntransforms = std::min(transforms_obj.length(), N); + size_t i; + + // Convert all of the transforms up front + typedef std::vector<agg::trans_affine> transforms_t; + transforms_t transforms; + transforms.reserve(Ntransforms); + for (i = 0; i < Ntransforms; ++i) { + agg::trans_affine trans = py_to_agg_transformation_matrix + (transforms_obj[i], false); + trans *= master_transform; + transforms.push_back(trans); + } + + typedef std::vector<PathCollectionExtents> path_extents_t; + path_extents_t path_extents; + path_extents.resize(Npaths); + + // Get each of the path extents first + i = 0; + for (path_extents_t::iterator p = path_extents.begin(); + p != path_extents.end(); ++p, ++i) { + PathIterator path(paths[i]); + agg::trans_affine& trans = transforms[i % Ntransforms]; + ::get_path_extents(path, trans, &p->x0, &p->y0, &p->x1, &p->y1); + } + + // The offset each of those and collect the mins/maxs + double x0 = std::numeric_limits<double>::infinity(); + double y0 = std::numeric_limits<double>::infinity(); + double x1 = -std::numeric_limits<double>::infinity(); + double y1 = -std::numeric_limits<double>::infinity(); + for (i = 0; i < N; ++i) { + Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % Noffsets]); + double xo = Py::Float(offset[0]); + double yo = Py::Float(offset[1]); + offset_trans.transform(&xo, &yo); + PathCollectionExtents& ext = path_extents[i % Npaths]; + + x0 = std::min(x0, ext.x0 + xo); + y0 = std::min(y0, ext.y0 + yo); + x1 = std::max(x1, ext.x1 + xo); + y1 = std::max(y1, ext.y1 + yo); + } + + Py::Tuple result(4); + result[0] = Py::Float(x0); + result[1] = Py::Float(y0); + result[2] = Py::Float(x1); + result[3] = Py::Float(y1); + return result; +} + +Py::Object _backend_agg_module::point_in_path_collection(const Py::Tuple& args) { + args.verify_length(9); + + //segments, trans, clipbox, colors, linewidths, antialiaseds + double x = Py::Float(args[0]); + double y = Py::Float(args[1]); + double radius = Py::Float(args[2]); + agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[3]); + Py::SeqBase<Py::Object> paths = args[4]; + Py::SeqBase<Py::Object> transforms_obj = args[5]; + Py::SeqBase<Py::Object> offsets = args[6]; + agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7], false); + Py::SeqBase<Py::Object> facecolors = args[8]; + + size_t Npaths = paths.length(); + size_t Noffsets = offsets.length(); + size_t N = std::max(Npaths, Noffsets); + size_t Ntransforms = std::min(transforms_obj.length(), N); + size_t Ncolors = facecolors.length(); + size_t i; + + // Convert all of the transforms up front + typedef std::vector<agg::trans_affine> transforms_t; + transforms_t transforms; + transforms.reserve(Ntransforms); + for (i = 0; i < Ntransforms; ++i) { + agg::trans_affine trans = py_to_agg_transformation_matrix + (transforms_obj[i], false); + trans *= master_transform; + transforms.push_back(trans); + } + + Py::List result; + + for (i = 0; i < N; ++i) { + PathIterator path(paths[i % Npaths]); + + Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % Noffsets]); + double xo = Py::Float(offset[0]); + double yo = Py::Float(offset[1]); + offset_trans.transform(&xo, &yo); + agg::trans_affine_translation transOffset(xo, yo); + agg::trans_affine trans = transforms[i % Ntransforms] * transOffset; + + const Py::Object& facecolor_obj = facecolors[i & Ncolors]; + if (facecolor_obj.ptr() == Py_None) { + if (::point_on_path(x, y, radius, path, trans)) + result.append(Py::Int((int)i)); + } else { + if (::point_in_path(x, y, path, trans)) + result.append(Py::Int((int)i)); + } + } + + return result; +} + /* ------------ module methods ------------- */ Py::Object _backend_agg_module::new_renderer (const Py::Tuple &args, const Py::Dict &kws) @@ -1563,7 +1708,7 @@ add_varargs_method("draw_path", &RendererAgg::draw_path, "draw_path(gc, path, transform, rgbFace)\n"); add_varargs_method("draw_path_collection", &RendererAgg::draw_path_collection, - "draw_path_collection(master_transform, cliprect, clippath, clippath_trans, paths, transforms, facecolors, edgecolors, linewidths, linestyles, antialiaseds)\n"); + "draw_path_collection(master_transform, cliprect, clippath, clippath_trans, paths, transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds)\n"); add_varargs_method("draw_markers", &RendererAgg::draw_markers, "draw_markers(gc, marker_path, marker_trans, path, rgbFace)\n"); add_varargs_method("draw_text_image", &RendererAgg::draw_text_image, Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-10-08 12:45:23 UTC (rev 3927) +++ branches/transforms/src/_backend_agg.h 2007-10-08 18:10:11 UTC (rev 3928) @@ -244,6 +244,11 @@ "point_on_path(x, y, r, path, trans)"); add_varargs_method("get_path_extents", &_backend_agg_module::get_path_extents, "get_path_extents(path, trans)"); + add_varargs_method("get_path_collection_extents", &_backend_agg_module::get_path_collection_extents, + "get_path_collection_extents(trans, paths, transforms, offsets, offsetTrans)"); + add_varargs_method("point_in_path_collection", &_backend_agg_module::point_in_path_collection, + "point_in_path_collection(x, y, r, trans, paths, transforms, offsets, offsetTrans, colors)"); + initialize( "The agg rendering backend" ); } @@ -255,6 +260,8 @@ Py::Object point_in_path(const Py::Tuple& args); Py::Object point_on_path(const Py::Tuple& args); Py::Object get_path_extents(const Py::Tuple& args); + Py::Object get_path_collection_extents(const Py::Tuple& args); + Py::Object point_in_path_collection(const Py::Tuple& args); }; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-08 12:45:25
|
Revision: 3927 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3927&view=rev Author: mdboom Date: 2007-10-08 05:45:23 -0700 (Mon, 08 Oct 2007) Log Message: ----------- Save image resolution in the PNG file. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py trunk/matplotlib/src/_backend_agg.cpp Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-10-05 22:11:32 UTC (rev 3926) +++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-10-08 12:45:23 UTC (rev 3927) @@ -415,5 +415,5 @@ def print_png(self, filename, *args, **kwargs): self.draw() - self.get_renderer()._renderer.write_png(str(filename)) + self.get_renderer()._renderer.write_png(str(filename), self.figure.dpi.get()) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2007-10-05 22:11:32 UTC (rev 3926) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2007-10-08 12:45:23 UTC (rev 3927) @@ -97,6 +97,8 @@ 0, 0, 0, 0, w, h) if DEBUG: print 'FigureCanvasGTKAgg.done' + def print_png(self, filename, *args, **kwargs): + return FigureCanvasAgg.print_png(self, filename, *args, **kwargs) """\ Traceback (most recent call last): Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2007-10-05 22:11:32 UTC (rev 3926) +++ trunk/matplotlib/src/_backend_agg.cpp 2007-10-08 12:45:23 UTC (rev 3927) @@ -2283,7 +2283,7 @@ { _VERBOSE("RendererAgg::write_png"); - args.verify_length(1); + args.verify_length(1, 2); FILE *fp; Py::Object o = Py::Object(args[0]); @@ -2344,6 +2344,13 @@ width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + // Save the dpi of the image in the file + if (args.size() == 2) { + double dpi = Py::Float(args[1]); + size_t dots_per_meter = (size_t)(dpi / (2.54 / 100.0)); + png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); + } // this a a color image! sig_bit.gray = 0; @@ -2619,7 +2626,7 @@ add_varargs_method("write_rgba", &RendererAgg::write_rgba, "write_rgba(fname)"); add_varargs_method("write_png", &RendererAgg::write_png, - "write_png(fname)"); + "write_png(fname, dpi=None)"); add_varargs_method("tostring_rgb", &RendererAgg::tostring_rgb, "s = tostring_rgb()"); add_varargs_method("tostring_argb", &RendererAgg::tostring_argb, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2007-10-05 22:11:35
|
Revision: 3926 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3926&view=rev Author: efiring Date: 2007-10-05 15:11:32 -0700 (Fri, 05 Oct 2007) Log Message: ----------- Fixed numpification bug in pcolor argument handling Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-10-05 19:37:18 UTC (rev 3925) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-10-05 22:11:32 UTC (rev 3926) @@ -4408,7 +4408,30 @@ return im + def _pcolorargs(self, funcname, *args): + if len(args)==1: + C = args[0] + numRows, numCols = C.shape + X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) + elif len(args)==3: + X, Y, C = args + else: + raise TypeError( + 'Illegal arguments to %s; see help(%s)' % (funcname, funcname)) + Nx = X.shape[-1] + Ny = Y.shape[0] + if len(X.shape) <> 2 or X.shape[0] == 1: + x = X.reshape(1,Nx) + X = x.repeat(Ny, axis=0) + if len(Y.shape) <> 2 or Y.shape[1] == 1: + y = Y.reshape(Ny, 1) + Y = y.repeat(Nx, axis=1) + if X.shape != Y.shape: + raise TypeError( + 'Incompatible X, Y inputs to %s; see help(%s)' % (funcname, funcname)) + return X, Y, C + def pcolor(self, *args, **kwargs): """ pcolor(*args, **kwargs): pseudocolor plot of a 2-D array @@ -4520,25 +4543,9 @@ vmax = kwargs.pop('vmax', None) shading = kwargs.pop('shading', 'flat') - if len(args)==1: - C = args[0] - numRows, numCols = C.shape - X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) - elif len(args)==3: - X, Y, C = args - numRows, numCols = C.shape - else: - raise TypeError, 'Illegal arguments to pcolor; see help(pcolor)' + X, Y, C = self._pcolorargs('pcolor', *args) + Ny, Nx = X.shape - Nx = X.shape[-1] - Ny = Y.shape[0] - if len(X.shape) <> 2 or X.shape[0] == 1: - X = X.ravel().resize((Ny, Nx)) - if len(Y.shape) <> 2 or Y.shape[1] == 1: - Y = Y.ravel().resize((Nx, Ny)).T - - - # convert to MA, if necessary. C = ma.asarray(C) X = ma.asarray(X) @@ -4673,23 +4680,9 @@ shading = kwargs.pop('shading', 'flat') edgecolors = kwargs.pop('edgecolors', 'None') - if len(args)==1: - C = args[0] - numRows, numCols = C.shape - X, Y = npy.meshgrid(npy.arange(numCols+1), npy.arange(numRows+1) ) - elif len(args)==3: - X, Y, C = args - numRows, numCols = C.shape - else: - raise TypeError, 'Illegal arguments to pcolormesh; see help(pcolormesh)' + X, Y, C = self._pcolorargs('pcolormesh', *args) + Ny, Nx = X.shape - Nx = X.shape[-1] - Ny = Y.shape[0] - if len(X.shape) <> 2 or X.shape[0] == 1: - X = X.ravel().resize((Ny, Nx)) - if len(Y.shape) <> 2 or Y.shape[1] == 1: - Y = Y.ravel().resize((Nx, Ny)).T - # convert to one dimensional arrays C = ma.ravel(C[0:Ny-1, 0:Nx-1]) # data point in each cell is value at lower left corner X = X.ravel() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-05 19:37:21
|
Revision: 3925 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3925&view=rev Author: mdboom Date: 2007-10-05 12:37:18 -0700 (Fri, 05 Oct 2007) Log Message: ----------- Merged revisions 3909-3924 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3910 | jrevans | 2007-10-03 18:23:48 -0400 (Wed, 03 Oct 2007) | 4 lines Moved a couple of routines from the Agg version of the FigureCanvas to the base qt version where they belong. Added a couple of overloaded qt methods that should be there and reduce having to inherit when embedding in another QWidget. ........ r3911 | jrevans | 2007-10-03 19:05:30 -0400 (Wed, 03 Oct 2007) | 3 lines Removed an erroneous print statment in backend_qt.py. Added a feature to keep track of axes inversions. ........ r3916 | sameerd | 2007-10-04 17:39:07 -0400 (Thu, 04 Oct 2007) | 3 lines Fix for "NameError: global name 'ones' is not defined" ........ r3917 | jrevans | 2007-10-04 18:13:18 -0400 (Thu, 04 Oct 2007) | 5 lines axes.py: Reverted get/set xlim/ylim methods to original state Added get/set xbound/ybound to handle axes inversion maintenance Removed inverted axes flags patches.py: Added some logic to xform an Ellipse angle as per the Ellipse's transformation. ........ r3918 | efiring | 2007-10-05 02:18:25 -0400 (Fri, 05 Oct 2007) | 2 lines Minor cleanup of arguments and docstring in contour ........ r3919 | efiring | 2007-10-05 02:58:15 -0400 (Fri, 05 Oct 2007) | 2 lines Tweaked automatic contour level calculation ........ r3920 | jrevans | 2007-10-05 12:29:17 -0400 (Fri, 05 Oct 2007) | 2 lines Fixed a typo in the Ellipse code that was causing the ellipse angle to go in the wrong direction. When I was testing I had forgotten to turn off the axes inversion test. ........ r3921 | jrevans | 2007-10-05 13:01:36 -0400 (Fri, 05 Oct 2007) | 2 lines Fixed an error in calculating the mid-point of a bar since the values are now lists and not arrays, they need to be iterated to perform the arithmetic. ........ r3923 | dsdale | 2007-10-05 14:56:10 -0400 (Fri, 05 Oct 2007) | 1 line remove generator expressions from texmanager and mpltraits ........ Modified Paths: -------------- branches/transforms/CHANGELOG branches/transforms/examples/simple_plot.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/config/mpltraits.py branches/transforms/lib/matplotlib/contour.py branches/transforms/lib/matplotlib/texmanager.py branches/transforms/lib/matplotlib/ticker.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3908 + /trunk/matplotlib:1-3924 Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/CHANGELOG 2007-10-05 19:37:18 UTC (rev 3925) @@ -1,3 +1,6 @@ +2007-10-05 remove generator expressions from texmanager and mpltraits. + generator expressions are not supported by python-2.3 - DSD + 2007-10-01 Made matplotlib.use() raise an exception if called after backends has been imported. Modified: branches/transforms/examples/simple_plot.py =================================================================== --- branches/transforms/examples/simple_plot.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/examples/simple_plot.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -17,4 +17,6 @@ #savefig('simple_plot.png') savefig('simple_plot') +axes().set_xlim(5, -5) + show() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -844,9 +844,9 @@ return - xmin,xmax = self.get_xlim() + xmin,xmax = self.get_xbound() xsize = max(math.fabs(xmax-xmin), 1e-30) - ymin,ymax = self.get_ylim() + ymin,ymax = self.get_ybound() ysize = max(math.fabs(ymax-ymin), 1e-30) l,b,w,h = self.get_position(original=True).bounds @@ -895,14 +895,14 @@ yc = 0.5*(ymin+ymax) y0 = yc - Ysize/2.0 y1 = yc + Ysize/2.0 - self.set_ylim((y0, y1)) + self.set_ybound((y0, y1)) #print 'New y0, y1:', y0, y1 #print 'New ysize, ysize/xsize', y1-y0, (y1-y0)/xsize else: xc = 0.5*(xmin+xmax) x0 = xc - Xsize/2.0 x1 = xc + Xsize/2.0 - self.set_xlim((x0, x1)) + self.set_xbound((x0, x1)) #print 'New x0, x1:', x0, x1 #print 'New xsize, ysize/xsize', x1-x0, ysize/(x1-x0) @@ -1186,24 +1186,20 @@ len(self.lines)==0 and len(self.patches)==0)): - if scalex: self.set_xlim(self.dataLim.intervalx().get_bounds()) + if scalex: self.set_xbound(self.dataLim.intervalx().get_bounds()) - if scaley: self.set_ylim(self.dataLim.intervaly().get_bounds()) + if scaley: self.set_ybound(self.dataLim.intervaly().get_bounds()) return if scalex: - xl = self.get_xlim() + xl = self.get_xbound() XL = self.xaxis.get_major_locator().autoscale() - if xl[1] < xl[0]: - XL = XL[::-1] - self.set_xlim(XL) + self.set_xbound(XL) if scaley: ylocator = self.yaxis.get_major_locator() - yl = self.get_ylim() + yl = self.get_ybound() YL = ylocator.autoscale() - if yl[1] < yl[0]: - YL = YL[::-1] - self.set_ylim(YL) + self.set_ybound(YL) #### Drawing def draw(self, renderer=None, inframe=False): @@ -1427,14 +1423,47 @@ ### data limits, ticks, tick labels, and formatting - def invert_xaxis(self, invert=True): - "Invert the x-axis if 'invert' is True." - self._invertedx = invert + def invert_xaxis(self): + "Invert the x-axis." + left, right = self.get_xlim() + self.set_xlim(right, left) def xaxis_inverted(self): 'Returns True if the x-axis is inverted.' - return self._invertedx + left, right = self.get_xlim() + return right < left + def get_xbound(self): + "Returns the x-axis numerical bounds in the form of lowerBound < upperBound" + left, right = self.get_xlim() + if left < right: + return left, right + else: + return right, left + + def set_xbound(self, lower=None, upper=None): + """Set the lower and upper numerical bounds of the x-axis. + This method will honor axes inversion regardless of parameter order. + """ + if upper is None and iterable(lower): + lower,upper = lower + + old_lower,old_upper = self.get_xbound() + + if lower is None: lower = old_lower + if upper is None: upper = old_upper + + if self.xaxis_inverted(): + if lower < upper: + self.set_xlim(upper, lower) + else: + self.set_xlim(lower, upper) + else: + if lower < upper: + self.set_xlim(lower, upper) + else: + self.set_xlim(upper, lower) + def get_xlim(self): """Get the x-axis range [xmin, xmax] @@ -1483,21 +1512,9 @@ if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax - # provided for backwards compatability - if ( xmax < xmin ): - # swap the values so that xmin < xmax and set inverted flag - tmp = xmin - xmin = xmax - xmax = tmp - self.invert_xaxis( True ) - - if ( self._invertedx ): - xmax, xmin = mtransforms.nonsingular(xmax, xmin, increasing=False) - self.viewLim.intervalx = (xmax, xmin) - else: - xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False) - self.viewLim.intervalx = (xmin, xmax) - + xmax, xmin = mtransforms.nonsingular(xmax, xmin, increasing=False) + self.viewLim.intervalx = (xmin, xmax) + if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one @@ -1566,14 +1583,47 @@ return self.xaxis.set_ticklabels(labels, fontdict, **kwargs) set_xticklabels.__doc__ = cbook.dedent(set_xticklabels.__doc__) % martist.kwdocd - def invert_yaxis(self, invert=True): - "Invert the y-axis if 'invert' is True." - self._invertedy = invert + def invert_yaxis(self): + "Invert the y-axis." + left, right = self.get_ylim() + self.set_ylim(right, left) def yaxis_inverted(self): 'Returns True if the y-axis is inverted.' - return self._invertedy + left, right = self.get_ylim() + return right < left + def get_ybound(self): + "Returns the y-axis numerical bounds in the form of lowerBound < upperBound" + left, right = self.get_ylim() + if left < right: + return left, right + else: + return right, left + + def set_ybound(self, lower=None, upper=None): + """Set the lower and upper numerical bounds of the y-axis. + This method will honor axes inversion regardless of parameter order. + """ + if upper is None and iterable(lower): + lower,upper = lower + + old_lower,old_upper = self.get_ybound() + + if lower is None: lower = old_lower + if upper is None: upper = old_upper + + if self.yaxis_inverted(): + if lower < upper: + self.set_ylim(upper, lower) + else: + self.set_ylim(lower, upper) + else: + if lower < upper: + self.set_ylim(lower, upper) + else: + self.set_ylim(upper, lower) + def get_ylim(self): """Get the y-axis range [xmin, xmax] @@ -1620,21 +1670,9 @@ if ymin is None: ymin = old_ymin if ymax is None: ymax = old_ymax - # provided for backwards compatability - if ( ymax < ymin ): - # swap the values so that ymin < ymax and set inverted flag - tmp = ymin - ymin = ymax - ymax = tmp - self.invert_yaxis( True ) + ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) + self.viewLim.intervaly = (ymin, ymax) - if ( self._invertedy ): - ymax, ymin = mtransforms.nonsingular(ymax, ymin, increasing=False) - self.viewLim.intervaly = (ymax, ymin) - else: - ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) - self.viewLim.intervaly = (ymin, ymax) - if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one @@ -2357,9 +2395,9 @@ y = npy.asarray(y) if len(xmin)==1: - xmin = xmin*ones(y.shape, y.dtype) + xmin = xmin*npy.ones(y.shape, y.dtype) if len(xmax)==1: - xmax = xmax*ones(y.shape, y.dtype) + xmax = xmax*npy.ones(y.shape, y.dtype) xmin = npy.asarray(xmin) xmax = npy.asarray(xmax) @@ -3199,9 +3237,9 @@ pass elif align == 'center': if orientation == 'vertical': - left = left - width/2. + left = [left[i] - width[i]/2. for i in range(len(left))] elif orientation == 'horizontal': - bottom = bottom-height/2. + bottom = [bottom[i] - height[i]/2. for i in range(len(bottom))] else: raise ValueError, 'invalid alignment: %s' % align Modified: branches/transforms/lib/matplotlib/config/mpltraits.py =================================================================== --- branches/transforms/lib/matplotlib/config/mpltraits.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/lib/matplotlib/config/mpltraits.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -46,7 +46,7 @@ def info(self): be = self.backends.keys() be.sort - return "one of %s"% ', '.join('%s'%i for i in be) + return "one of %s"% ', '.join(['%s'%i for i in be]) class BoolHandler(T.TraitHandler): @@ -73,7 +73,7 @@ return self.error(object, name, value) def info(self): - return "one of %s"% ', '.join('%s'%i for i in self.bools.keys()) + return "one of %s"% ', '.join(['%s'%i for i in self.bools.keys()]) flexible_true = T.Trait(True, BoolHandler()) flexible_false = T.Trait(False, BoolHandler()) Modified: branches/transforms/lib/matplotlib/contour.py =================================================================== --- branches/transforms/lib/matplotlib/contour.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/lib/matplotlib/contour.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -397,12 +397,7 @@ cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) norm = kwargs.get('norm', None) - self.clip_ends = kwargs.get('clip_ends', None) ######## self.extend = kwargs.get('extend', 'neither') - if self.clip_ends is not None: - warnings.warn("'clip_ends' has been replaced by 'extend'") - self.levels = self.levels[1:-1] # discard specified end levels - self.extend = 'both' # regenerate end levels self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) self.locator = kwargs.get('locator', None) @@ -436,10 +431,8 @@ _mask = None if self.filled: - if self.linewidths is None: - self.linewidths = 0.05 # Good default for Postscript. - if cbook.iterable(self.linewidths): - self.linewidths = self.linewidths[0] + if self.linewidths is not None: + warnings.warn('linewidths is ignored by contourf') C = _cntr.Cntr(x, y, z.filled(), _mask) lowers = self._levels[:-1] uppers = self._levels[1:] @@ -447,7 +440,6 @@ nlist = C.trace(level, level_upper, points = 0, nchunk = self.nchunk) col = collections.PolyCollection(nlist, - linewidths = (self.linewidths,), antialiaseds = (self.antialiased,), edgecolors= 'None') self.ax.add_collection(col) @@ -500,16 +492,18 @@ one contour line, but two filled regions, and therefore three levels to provide boundaries for both regions. ''' - zmax = self.zmax - zmin = self.zmin - zmargin = (zmax - zmin) * 0.001 # so z < (zmax + zmargin) - zmax = zmax + zmargin - intv = transforms.Interval(transforms.Value(zmin), transforms.Value(zmax)) if self.locator is None: self.locator = ticker.MaxNLocator(N+1) - self.locator.set_view_interval(intv) - self.locator.set_data_interval(intv) - lev = self.locator() + locator = self.locator + zmax = self.zmax + zmin = self.zmin + locator.set_bounds(zmin, zmax) + lev = locator() + zmargin = (zmax - zmin) * 0.000001 # so z < (zmax + zmargin) + if zmax >= lev[-1]: + lev[-1] += zmargin + if zmin <= lev[0]: + lev[0] -= zmargin self._auto = True if self.filled: return lev @@ -627,16 +621,16 @@ self._levels = npy.asarray(self._levels) self.vmin = npy.amin(self.levels) # alternative would be self.layers self.vmax = npy.amax(self.levels) - if self.extend in ('both', 'min') or self.clip_ends: + if self.extend in ('both', 'min'): self.vmin = 2 * self.levels[0] - self.levels[1] - if self.extend in ('both', 'max') or self.clip_ends: + if self.extend in ('both', 'max'): self.vmax = 2 * self.levels[-1] - self.levels[-2] self.layers = self._levels # contour: a line is a thin layer if self.filled: self.layers = 0.5 * (self._levels[:-1] + self._levels[1:]) - if self.extend in ('both', 'min') or self.clip_ends: + if self.extend in ('both', 'min'): self.layers[0] = 0.5 * (self.vmin + self._levels[1]) - if self.extend in ('both', 'max') or self.clip_ends: + if self.extend in ('both', 'max'): self.layers[-1] = 0.5 * (self.vmax + self._levels[-2]) return (x, y, z) @@ -774,7 +768,6 @@ contour levels if they are not given explicitly via the V argument. - ***** New: ***** * extend = 'neither', 'both', 'min', 'max' Unless this is 'neither' (default), contour levels are automatically added to one or both ends of the range so that @@ -782,8 +775,7 @@ mapped to the special colormap values which default to the ends of the colormap range, but can be set via Colormap.set_under() and Colormap.set_over() methods. - To replace clip_ends=True and V = [-100, 2, 1, 0, 1, 2, 100], - use extend='both' and V = [2, 1, 0, 1, 2]. + **************** contour only: @@ -799,29 +791,13 @@ matplotlibrc is used contourf only: - ***** Obsolete: **** - * 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. - * linewidths = None or a number; default of 0.05 works for - Postscript; a value of about 0.5 seems better for Agg. - * antialiased = True (default) or False; if False, there is - no need to increase the linewidths for Agg, but True gives - nicer color boundaries. If antialiased is True and linewidths - is too small, then there may be light-colored lines at the - color boundaries caused by the antialiasing. + * antialiased = True (default) or False * nchunk = 0 (default) for no subdivision of the domain; specify a positive integer to divide the domain into subdomains of roughly nchunk by nchunk points. This may never actually be advantageous, so this option may be removed. Chunking introduces artifacts at the chunk - boundaries unless antialiased = False, or linewidths is - set to a large enough value for the particular renderer and - resolution. + boundaries unless antialiased = False """ Modified: branches/transforms/lib/matplotlib/texmanager.py =================================================================== --- branches/transforms/lib/matplotlib/texmanager.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/lib/matplotlib/texmanager.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -110,7 +110,7 @@ _rc_cache = None _rc_cache_keys = ('text.latex.preamble', )\ - + tuple('font.'+n for n in ('family', ) + font_families) + + tuple(['font.'+n for n in ('family', ) + font_families]) def __init__(self): @@ -125,7 +125,7 @@ fontconfig = [self.font_family] for font_family, font_family_attr in \ - ((ff, ff.replace('-', '_')) for ff in self.font_families): + [(ff, ff.replace('-', '_')) for ff in self.font_families]: for font in rcParams['font.'+font_family]: if font.lower() in self.font_info: found_font = self.font_info[font.lower()] @@ -163,7 +163,7 @@ def get_font_config(self): "Reinitializes self if rcParams self depends on have changed." if self._rc_cache is None: - self._rc_cache = dict((k,None) for k in self._rc_cache_keys) + self._rc_cache = dict([(k,None) for k in self._rc_cache_keys]) changed = [par for par in self._rc_cache_keys if rcParams[par] != \ self._rc_cache[par]] if changed: Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-10-05 19:25:33 UTC (rev 3924) +++ branches/transforms/lib/matplotlib/ticker.py 2007-10-05 19:37:18 UTC (rev 3925) @@ -370,7 +370,7 @@ self.format = '$%s$' % self.format elif self._useMathText: self.format = '$\mathdefault{%s}$' % self.format - + def pprint_val(self, x): xp = (x-self.offset)/10**self.orderOfMagnitude if npy.absolute(xp) < 1e-8: xp = 0 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-05 19:25:35
|
Revision: 3924 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3924&view=rev Author: mdboom Date: 2007-10-05 12:25:33 -0700 (Fri, 05 Oct 2007) Log Message: ----------- Simplified sharing axes again. Plowing through -- making more examples work. First pass at updating collections (LineCollection mostly works) Modified Paths: -------------- branches/transforms/examples/clippath_test.py branches/transforms/lib/matplotlib/artist.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/image.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/projections/polar.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Added Paths: ----------- branches/transforms/src/agg_py_path_iterator.h Modified: branches/transforms/examples/clippath_test.py =================================================================== --- branches/transforms/examples/clippath_test.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/examples/clippath_test.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -13,7 +13,7 @@ self.poly = RegularPolygon( (200, 200), numVertices=10, radius=100, facecolor='yellow', alpha=0.25, - transform=transforms.identity_transform()) + transform=transforms.IdentityTransform()) ax.add_patch(self.poly) self.canvas.mpl_connect('button_press_event', self.onpress) @@ -21,7 +21,6 @@ self.canvas.mpl_connect('motion_notify_event', self.onmove) self.x, self.y = None, None - def onpress(self, event): self.x, self.y = event.x, event.y @@ -42,17 +41,7 @@ self._clip() def _clip(self): - fig = self.ax.figure - l,b,w,h = fig.bbox.get_bounds() - path = agg.path_storage() - - for i, xy in enumerate(self.poly.get_verts()): - x,y = xy - y = h-y - if i==0: path.move_to(x,y) - else: path.line_to(x,y) - path.close_polygon() - self.line.set_clip_path(path) + self.line.set_clip_path(self.poly.get_path(), self.poly.get_transform()) self.canvas.draw_idle() Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/artist.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -335,6 +335,11 @@ 'Return artist clip path' return self._clippath + def get_transformed_clip_path_and_affine(self): + if self._clippath is not None: + return self._clippath.get_transformed_path_and_affine() + return None, None + def set_clip_on(self, b): """ Set whether artist uses clipping Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -488,13 +488,9 @@ self._sharey = sharey if sharex is not None: self._shared_x_axes.join(self, sharex) - if sharey is not None: + if sharey is not None: self._shared_y_axes.join(self, sharey) - # Flag: True if some other Axes instance is sharing our x or y axis - self._masterx = False - self._mastery = False - if sharex: sharex._masterx = True - if sharey: sharey._mastery = True + self.set_label(label) self.set_figure(fig) @@ -822,7 +818,8 @@ Use self._aspect and self._adjustable to modify the axes box or the view limits. ''' - + #MGDTODO: Numpify + if self._aspect == 'auto': self.set_position( self._originalPosition , 'active') return @@ -834,7 +831,7 @@ #Ensure at drawing time that any Axes involved in axis-sharing # does not have its position changed. - if self._masterx or self._mastery or self._sharex or self._sharey: + if self in self._shared_x_axes or self in self._shared_y_axes: self._adjustable = 'datalim' figW,figH = self.get_figure().get_size_inches() @@ -847,6 +844,11 @@ return + xmin,xmax = self.get_xlim() + xsize = max(math.fabs(xmax-xmin), 1e-30) + ymin,ymax = self.get_ylim() + ysize = max(math.fabs(ymax-ymin), 1e-30) + l,b,w,h = self.get_position(original=True).bounds box_aspect = fig_aspect * (h/w) data_ratio = box_aspect / A @@ -858,8 +860,8 @@ #print 'good enough already' return dL = self.dataLim - xr = 1.05 * dL.width() - yr = 1.05 * dL.height() + xr = 1.05 * dL.width + yr = 1.05 * dL.height xmarg = xsize - xr ymarg = ysize - yr Ysize = data_ratio * xsize @@ -871,10 +873,10 @@ #print 'xmin, xmax, ymin, ymax', xmin, xmax, ymin, ymax #print 'xsize, Xsize, ysize, Ysize', xsize, Xsize, ysize, Ysize - changex = ((self._sharey or self._mastery) and not - (self._sharex or self._masterx)) - changey = ((self._sharex or self._masterx) and not - (self._sharey or self._mastery)) + changex = (self in self._shared_y_axes + and self not in self._shared_x_axes) + changey = (self in self._shared_x_axes + and self not in self._shared_y_axes) if changex and changey: warnings.warn("adjustable='datalim' cannot work with shared x and y axes") return @@ -2200,7 +2202,8 @@ %(Line2D)s """ - trans = mtransforms.blend_xy_sep_transform(self.transAxes, self.transData) + trans = mtransforms.blended_transform_factory( + self.transAxes, self.transData) l, = self.plot([xmin,xmax], [y,y], transform=trans, scalex=False, **kwargs) return l @@ -2236,7 +2239,8 @@ %(Line2D)s """ - trans = mtransforms.blend_xy_sep_transform( self.transData, self.transAxes ) + trans = mtransforms.blended_transform_factory( + self.transData, self.transAxes) l, = self.plot([x,x], [ymin,ymax] , transform=trans, scaley=False, **kwargs) return l @@ -2275,7 +2279,8 @@ %(Polygon)s """ # convert y axis units - trans = mtransforms.blend_xy_sep_transform( self.transAxes, self.transData) + trans = mtransforms.blended_transform_factory( + self.transAxes, self.transData) verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) @@ -2315,7 +2320,8 @@ %(Polygon)s """ # convert x axis units - trans = mtransforms.blend_xy_sep_transform(self.transData, self.transAxes) + trans = mtransforms.blended_transform_factory( + self.transData, self.transAxes) verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) @@ -2386,7 +2392,7 @@ return coll hlines.__doc__ = cbook.dedent(hlines.__doc__) - def vlines(self, x, ymin, ymax, colors='k', linestyle='solid', + def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): """ VLINES(x, ymin, ymax, color='k') @@ -2438,7 +2444,7 @@ for thisx, (thisymin, thisymax) in zip(x,Y)] #print 'creating line collection' coll = mcoll.LineCollection(verts, colors=colors, - linestyle=linestyle, label=label) + linestyles=linestyles, label=label) self.add_collection(coll) coll.update(kwargs) @@ -3660,7 +3666,7 @@ else: lower = y-yerr[0] upper = y+yerr[1] - + barcols.append( self.vlines(x, lower, upper, **lines_kw) ) if capsize > 0: Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -1098,9 +1098,9 @@ default resets the tick positions to the default: ticks on both positions, labels at bottom. - ACCEPTS: [ 'top' | 'bottom' | 'both' | 'default' ] + ACCEPTS: [ 'top' | 'bottom' | 'both' | 'default' | 'none' ] """ - assert position == 'top' or position == 'bottom' or position == 'both' or position == 'default' + assert position in ('top', 'bottom', 'both', 'default', 'none') ticks = list( self.get_major_ticks() ) # a copy @@ -1124,6 +1124,10 @@ t.tick2On = True t.label1On = True t.label2On = False + elif position == 'none': + for t in ticks: + t.tick1On = False + t.tick2On = False else: for t in ticks: t.tick1On = True @@ -1306,9 +1310,9 @@ default resets the tick positions to the default: ticks on both positions, labels on the left. - ACCEPTS: [ 'left' | 'right' | 'both' | 'default' ] + ACCEPTS: [ 'left' | 'right' | 'both' | 'default' | 'none' ] """ - assert position == 'left' or position == 'right' or position == 'both' or position == 'default' + assert position in ('left', 'right', 'both', 'default', 'none') ticks = list( self.get_major_ticks() ) # a copy ticks.extend( self.get_minor_ticks() ) @@ -1334,6 +1338,10 @@ t.tick2On = True t.label1On = True t.label2On = False + elif position == 'none': + for t in ticks: + t.tick1On = False + t.tick2On = False else: self.set_offset_position('left') for t in ticks: Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -83,322 +83,322 @@ ## These functions no longer need to be implemented in the backends -- ## they now perform all of their functions in terms of the new API. - def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, - rotation): - """ - Draw an arc using GraphicsContext instance gcEdge, centered at x,y, - with width and height and angles from 0.0 to 360.0 - 0 degrees is at 3-o'clock - positive angles are anti-clockwise - draw rotated 'rotation' degrees anti-clockwise about x,y +# def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, +# rotation): +# """ +# Draw an arc using GraphicsContext instance gcEdge, centered at x,y, +# with width and height and angles from 0.0 to 360.0 +# 0 degrees is at 3-o'clock +# positive angles are anti-clockwise +# draw rotated 'rotation' degrees anti-clockwise about x,y - If the color rgbFace is not None, fill the arc with it. - """ - raise NotImplementedError +# If the color rgbFace is not None, fill the arc with it. +# """ +# raise NotImplementedError - def draw_line_collection(self, segments, transform, clipbox, - colors, linewidths, linestyle, antialiaseds, - offsets, transOffset): - """ - This is a function for optimized line drawing. If you need to draw - many line segments with similar properties, it is faster to avoid the - overhead of all the object creation etc. The lack of total - configurability is compensated for with efficiency. Hence we don't use - a GC and many of the line props it supports. See - matplotlib.collections for more details. +# def draw_line_collection(self, segments, transform, clipbox, +# colors, linewidths, linestyle, antialiaseds, +# offsets, transOffset): +# """ +# This is a function for optimized line drawing. If you need to draw +# many line segments with similar properties, it is faster to avoid the +# overhead of all the object creation etc. The lack of total +# configurability is compensated for with efficiency. Hence we don't use +# a GC and many of the line props it supports. See +# matplotlib.collections for more details. - segments is a sequence of ( line0, line1, line2), where linen = - is an Mx2 array with columns x, y. Each line can be a - different length +# segments is a sequence of ( line0, line1, line2), where linen = +# is an Mx2 array with columns x, y. Each line can be a +# different length - transform is used to Transform the lines +# transform is used to Transform the lines - clipbox is a xmin, ymin, width, height clip rect +# clipbox is a xmin, ymin, width, height clip rect - colors is a tuple of RGBA tuples +# colors is a tuple of RGBA tuples - linewidths is a tuple of linewidths - *** really should be called 'dashes' not 'linestyle', since - we call gc.set_dashes() not gc.set_linestyle() *** +# linewidths is a tuple of linewidths +# *** really should be called 'dashes' not 'linestyle', since +# we call gc.set_dashes() not gc.set_linestyle() *** - linestyle is an (offset, onoffseq) tuple or None,None for solid +# linestyle is an (offset, onoffseq) tuple or None,None for solid - antialiseds is a tuple of ones or zeros indicating whether the - segment should be aa or not +# antialiseds is a tuple of ones or zeros indicating whether the +# segment should be aa or not - offsets, if not None, is an Nx2 array of x,y offsets to - translate the lines by after transform is used to transform - the offset coords +# offsets, if not None, is an Nx2 array of x,y offsets to +# translate the lines by after transform is used to transform +# the offset coords - This function could be overridden in the backend to possibly implement - faster drawing, but it is already much faster than using draw_lines() - by itself. - """ +# This function could be overridden in the backend to possibly implement +# faster drawing, but it is already much faster than using draw_lines() +# by itself. +# """ - newstyle = getattr(self, 'draw_markers', None) is not None - identity = transforms.identity_transform() - gc = self.new_gc() - if clipbox is not None: - gc.set_clip_rectangle(clipbox.get_bounds()) - gc.set_dashes(*linestyle) +# newstyle = getattr(self, 'draw_markers', None) is not None +# identity = transforms.identity_transform() +# gc = self.new_gc() +# if clipbox is not None: +# gc.set_clip_rectangle(clipbox.get_bounds()) +# gc.set_dashes(*linestyle) - Nc = len(colors) - Nlw = len(linewidths) - Naa = len(antialiaseds) - Nsegments = len(segments) +# Nc = len(colors) +# Nlw = len(linewidths) +# Naa = len(antialiaseds) +# Nsegments = len(segments) - usingOffsets = offsets is not None - Noffsets = 0 - if usingOffsets: - Noffsets = offsets.shape[0] - offsets = transOffset.numerix_xy(offsets) +# usingOffsets = offsets is not None +# Noffsets = 0 +# if usingOffsets: +# Noffsets = offsets.shape[0] +# offsets = transOffset.numerix_xy(offsets) - for i in xrange(max(Noffsets, Nsegments)): - color = colors[i % Nc] - rgb = color[0], color[1], color[2] - alpha = color[-1] +# for i in xrange(max(Noffsets, Nsegments)): +# color = colors[i % Nc] +# rgb = color[0], color[1], color[2] +# alpha = color[-1] - gc.set_foreground(rgb, isRGB=True) - gc.set_alpha( alpha ) - gc.set_linewidth( linewidths[i % Nlw] ) - gc.set_antialiased( antialiaseds[i % Naa] ) - seg = segments[i % Nsegments] - if not len(seg): continue - xy = transform.numerix_xy(seg) - if usingOffsets: - xy = xy + offsets[i % Noffsets] +# gc.set_foreground(rgb, isRGB=True) +# gc.set_alpha( alpha ) +# gc.set_linewidth( linewidths[i % Nlw] ) +# gc.set_antialiased( antialiaseds[i % Naa] ) +# seg = segments[i % Nsegments] +# if not len(seg): continue +# xy = transform.numerix_xy(seg) +# if usingOffsets: +# xy = xy + offsets[i % Noffsets] - if newstyle: self.draw_lines(gc, xy[:,0], xy[:,1], identity) - else: self.draw_lines(gc, xy[:,0], xy[:,1]) +# if newstyle: self.draw_lines(gc, xy[:,0], xy[:,1], identity) +# else: self.draw_lines(gc, xy[:,0], xy[:,1]) - def draw_line(self, gc, x1, y1, x2, y2): - """ - Draw a single line from x1,y1 to x2,y2 - """ - raise NotImplementedError +# def draw_line(self, gc, x1, y1, x2, y2): +# """ +# Draw a single line from x1,y1 to x2,y2 +# """ +# raise NotImplementedError - def draw_lines(self, gc, x, y, transform=None): - """ - x and y are equal length arrays, draw lines connecting each - point in x, y - """ - raise NotImplementedError +# def draw_lines(self, gc, x, y, transform=None): +# """ +# x and y are equal length arrays, draw lines connecting each +# point in x, y +# """ +# raise NotImplementedError - def draw_point(self, gc, x, y): - """ - Draw a single point at x,y - Where 'point' is a device-unit point (or pixel), not a matplotlib point - """ - raise NotImplementedError +# def draw_point(self, gc, x, y): +# """ +# Draw a single point at x,y +# Where 'point' is a device-unit point (or pixel), not a matplotlib point +# """ +# raise NotImplementedError - def draw_quad_mesh(self, meshWidth, meshHeight, colors, - xCoords, yCoords, clipbox, - transform, offsets, transOffset, showedges): - """ - Draw a quadrilateral mesh - See documentation in QuadMesh class in collections.py for details - """ - # print "draw_quad_mesh not found, using function in backend_bases" - verts = npy.zeros(((meshWidth * meshHeight), 4, 2), npy.float32) - indices = npy.arange((meshWidth + 1) * (meshHeight + 1)) - indices = npy.compress((indices + 1) % (meshWidth + 1), indices) - indices = indices[:(meshWidth * meshHeight)] - verts[:, 0, 0] = npy.take(xCoords, indices) - verts[:, 0, 1] = npy.take(yCoords, indices) - verts[:, 1, 0] = npy.take(xCoords, (indices + 1)) - verts[:, 1, 1] = npy.take(yCoords, (indices + 1)) - verts[:, 2, 0] = npy.take(xCoords, (indices + meshWidth + 2)) - verts[:, 2, 1] = npy.take(yCoords, (indices + meshWidth + 2)) - verts[:, 3, 0] = npy.take(xCoords, (indices + meshWidth + 1)) - verts[:, 3, 1] = npy.take(yCoords, (indices + meshWidth + 1)) - if (showedges): - edgecolors = colors - else: - edgecolors = (0, 0, 0, 0), - self.draw_poly_collection(verts, transform, - clipbox, colors, edgecolors, - (0.25,), (0,), offsets, transOffset) +# def draw_quad_mesh(self, meshWidth, meshHeight, colors, +# xCoords, yCoords, clipbox, +# transform, offsets, transOffset, showedges): +# """ +# Draw a quadrilateral mesh +# See documentation in QuadMesh class in collections.py for details +# """ +# # print "draw_quad_mesh not found, using function in backend_bases" +# verts = npy.zeros(((meshWidth * meshHeight), 4, 2), npy.float32) +# indices = npy.arange((meshWidth + 1) * (meshHeight + 1)) +# indices = npy.compress((indices + 1) % (meshWidth + 1), indices) +# indices = indices[:(meshWidth * meshHeight)] +# verts[:, 0, 0] = npy.take(xCoords, indices) +# verts[:, 0, 1] = npy.take(yCoords, indices) +# verts[:, 1, 0] = npy.take(xCoords, (indices + 1)) +# verts[:, 1, 1] = npy.take(yCoords, (indices + 1)) +# verts[:, 2, 0] = npy.take(xCoords, (indices + meshWidth + 2)) +# verts[:, 2, 1] = npy.take(yCoords, (indices + meshWidth + 2)) +# verts[:, 3, 0] = npy.take(xCoords, (indices + meshWidth + 1)) +# verts[:, 3, 1] = npy.take(yCoords, (indices + meshWidth + 1)) +# if (showedges): +# edgecolors = colors +# else: +# edgecolors = (0, 0, 0, 0), +# self.draw_poly_collection(verts, transform, +# clipbox, colors, edgecolors, +# (0.25,), (0,), offsets, transOffset) - def draw_poly_collection( - self, verts, transform, clipbox, facecolors, edgecolors, - linewidths, antialiaseds, offsets, transOffset): - """ - Draw a polygon collection +# def draw_poly_collection( +# self, verts, transform, clipbox, facecolors, edgecolors, +# linewidths, antialiaseds, offsets, transOffset): +# """ +# Draw a polygon collection - verts are a sequence of polygon vectors, where each polygon - vector is a sequence of x,y tuples of vertices +# verts are a sequence of polygon vectors, where each polygon +# vector is a sequence of x,y tuples of vertices - facecolors and edgecolors are a sequence of RGBA tuples - linewidths are a sequence of linewidths - antialiaseds are a sequence of 0,1 integers whether to use aa +# facecolors and edgecolors are a sequence of RGBA tuples +# linewidths are a sequence of linewidths +# antialiaseds are a sequence of 0,1 integers whether to use aa - If a linewidth is zero or an edgecolor alpha is zero, the - line will be omitted; similarly, the fill will be omitted - if the facecolor alpha is zero. - """ - ## line and/or fill OK - Nface = len(facecolors) - Nedge = len(edgecolors) - Nlw = len(linewidths) - Naa = len(antialiaseds) +# If a linewidth is zero or an edgecolor alpha is zero, the +# line will be omitted; similarly, the fill will be omitted +# if the facecolor alpha is zero. +# """ +# ## line and/or fill OK +# Nface = len(facecolors) +# Nedge = len(edgecolors) +# Nlw = len(linewidths) +# Naa = len(antialiaseds) - usingOffsets = offsets is not None - Noffsets = 0 - Nverts = len(verts) - if usingOffsets: - Noffsets = len(offsets) +# usingOffsets = offsets is not None +# Noffsets = 0 +# Nverts = len(verts) +# if usingOffsets: +# Noffsets = len(offsets) - N = max(Noffsets, Nverts) +# N = max(Noffsets, Nverts) - gc = self.new_gc() - if clipbox is not None: - gc.set_clip_rectangle(clipbox.get_bounds()) +# gc = self.new_gc() +# if clipbox is not None: +# gc.set_clip_rectangle(clipbox.get_bounds()) - for i in xrange(N): - polyverts = ma.filled(verts[i % Nverts], npy.nan) - if npy.any(npy.isnan(polyverts)): - continue - linewidth = linewidths[i % Nlw] - rf,gf,bf,af = facecolors[i % Nface] - re,ge,be,ae = edgecolors[i % Nedge] - if af==0: - if ae==0 or linewidth == 0: - continue - rgbFace = None - alpha = ae - else: - rgbFace = rf,gf,bf - if ae==0: - alpha = af - gc.set_linewidth(0) - else: - # the draw_poly interface can't handle separate alphas for - # edge and face so we'll just use the maximum - alpha = max(af,ae) - gc.set_foreground( (re,ge,be), isRGB=True) - gc.set_linewidth( linewidths[i % Nlw] ) - #print 'verts', zip(thisxverts, thisyverts) +# for i in xrange(N): +# polyverts = ma.filled(verts[i % Nverts], npy.nan) +# if npy.any(npy.isnan(polyverts)): +# continue +# linewidth = linewidths[i % Nlw] +# rf,gf,bf,af = facecolors[i % Nface] +# re,ge,be,ae = edgecolors[i % Nedge] +# if af==0: +# if ae==0 or linewidth == 0: +# continue +# rgbFace = None +# alpha = ae +# else: +# rgbFace = rf,gf,bf +# if ae==0: +# alpha = af +# gc.set_linewidth(0) +# else: +# # the draw_poly interface can't handle separate alphas for +# # edge and face so we'll just use the maximum +# alpha = max(af,ae) +# gc.set_foreground( (re,ge,be), isRGB=True) +# gc.set_linewidth( linewidths[i % Nlw] ) +# #print 'verts', zip(thisxverts, thisyverts) - gc.set_antialiased( antialiaseds[i % Naa] ) # Used for fill only? - gc.set_alpha( alpha ) - tverts = transform.seq_xy_tups(polyverts) - if usingOffsets: - xo,yo = transOffset.xy_tup(offsets[i % Noffsets]) - tverts = [(x+xo,y+yo) for x,y in tverts] +# gc.set_antialiased( antialiaseds[i % Naa] ) # Used for fill only? +# gc.set_alpha( alpha ) +# tverts = transform.seq_xy_tups(polyverts) +# if usingOffsets: +# xo,yo = transOffset.xy_tup(offsets[i % Noffsets]) +# tverts = [(x+xo,y+yo) for x,y in tverts] - self.draw_polygon(gc, rgbFace, tverts) +# self.draw_polygon(gc, rgbFace, tverts) - def draw_polygon(self, gc, rgbFace, points): - """ - Draw a polygon using the GraphicsContext instance gc. - points is a len vertices tuple, each element - giving the x,y coords a vertex +# def draw_polygon(self, gc, rgbFace, points): +# """ +# Draw a polygon using the GraphicsContext instance gc. +# points is a len vertices tuple, each element +# giving the x,y coords a vertex - If the color rgbFace is not None, fill the polygon with it - """ - raise NotImplementedError +# If the color rgbFace is not None, fill the polygon with it +# """ +# raise NotImplementedError - def draw_rectangle(self, gcEdge, rgbFace, x, y, width, height): - """ - Draw a non-filled rectangle using the GraphicsContext instance gcEdge, - with lower left at x,y with width and height. +# def draw_rectangle(self, gcEdge, rgbFace, x, y, width, height): +# """ +# Draw a non-filled rectangle using the GraphicsContext instance gcEdge, +# with lower left at x,y with width and height. - If rgbFace is not None, fill the rectangle with it. - """ - warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning) - transform = transforms.Affine2D().scale(width, height).translate(x, y) - self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace) +# If rgbFace is not None, fill the rectangle with it. +# """ +# warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning) +# transform = transforms.Affine2D().scale(width, height).translate(x, y) +# self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace) - def draw_regpoly_collection( - self, clipbox, offsets, transOffset, verts, sizes, - facecolors, edgecolors, linewidths, antialiaseds): - """ - Draw a regular poly collection +# def draw_regpoly_collection( +# self, clipbox, offsets, transOffset, verts, sizes, +# facecolors, edgecolors, linewidths, antialiaseds): +# """ +# Draw a regular poly collection - offsets - is a sequence is x,y tuples - transOffset - maps this to display coords +# offsets - is a sequence is x,y tuples +# transOffset - maps this to display coords - verts - are the vertices of the regular polygon at the origin +# verts - are the vertices of the regular polygon at the origin - sizes are the area of the circle that circumscribes the - polygon in points^2 +# sizes are the area of the circle that circumscribes the +# polygon in points^2 - facecolors and edgecolors are a sequence of RGBA tuples - linewidths are a sequence of linewidths - antialiaseds are a sequence of 0,1 integers whether to use aa - """ - ## line and/or fill OK - gc = self.new_gc() - if clipbox is not None: - gc.set_clip_rectangle(clipbox.get_bounds()) +# facecolors and edgecolors are a sequence of RGBA tuples +# linewidths are a sequence of linewidths +# antialiaseds are a sequence of 0,1 integers whether to use aa +# """ +# ## line and/or fill OK +# gc = self.new_gc() +# if clipbox is not None: +# gc.set_clip_rectangle(clipbox.get_bounds()) - xverts, yverts = zip(*verts) - xverts = npy.asarray(xverts) - yverts = npy.asarray(yverts) +# xverts, yverts = zip(*verts) +# xverts = npy.asarray(xverts) +# yverts = npy.asarray(yverts) - Nface = len(facecolors) - Nedge = len(edgecolors) - Nlw = len(linewidths) - Naa = len(antialiaseds) - Nsizes = len(sizes) +# Nface = len(facecolors) +# Nedge = len(edgecolors) +# Nlw = len(linewidths) +# Naa = len(antialiaseds) +# Nsizes = len(sizes) - for i, loc in enumerate(offsets): - xo,yo = transOffset.xy_tup(loc) - #print 'xo, yo', loc, (xo, yo) - scale = sizes[i % Nsizes] +# for i, loc in enumerate(offsets): +# xo,yo = transOffset.xy_tup(loc) +# #print 'xo, yo', loc, (xo, yo) +# scale = sizes[i % Nsizes] - thisxverts = scale*xverts + xo - thisyverts = scale*yverts + yo - #print 'xverts', xverts +# thisxverts = scale*xverts + xo +# thisyverts = scale*yverts + yo +# #print 'xverts', xverts - linewidth = linewidths[i % Nlw] - rf,gf,bf,af = facecolors[i % Nface] - re,ge,be,ae = edgecolors[i % Nedge] - if af==0: - if ae==0 or linewidth == 0: - continue - rgbFace = None - alpha = ae - else: - rgbFace = rf,gf,bf - if ae==0: - alpha = af - gc.set_linewidth(0) - else: - # the draw_poly interface can't handle separate alphas for - # edge and face so we'll just use the maximum - alpha = max(af,ae) - gc.set_foreground( (re,ge,be), isRGB=True) - gc.set_linewidth( linewidths[i % Nlw] ) - #print 'verts', zip(thisxverts, thisyverts) +# linewidth = linewidths[i % Nlw] +# rf,gf,bf,af = facecolors[i % Nface] +# re,ge,be,ae = edgecolors[i % Nedge] +# if af==0: +# if ae==0 or linewidth == 0: +# continue +# rgbFace = None +# alpha = ae +# else: +# rgbFace = rf,gf,bf +# if ae==0: +# alpha = af +# gc.set_linewidth(0) +# else: +# # the draw_poly interface can't handle separate alphas for +# # edge and face so we'll just use the maximum +# alpha = max(af,ae) +# gc.set_foreground( (re,ge,be), isRGB=True) +# gc.set_linewidth( linewidths[i % Nlw] ) +# #print 'verts', zip(thisxverts, thisyverts) - gc.set_antialiased( antialiaseds[i % Naa] ) # Used for fill only? - gc.set_alpha( alpha ) - #print 'verts', zip(thisxverts, thisyverts) - self.draw_polygon(gc, rgbFace, zip(thisxverts, thisyverts)) +# gc.set_antialiased( antialiaseds[i % Naa] ) # Used for fill only? +# gc.set_alpha( alpha ) +# #print 'verts', zip(thisxverts, thisyverts) +# self.draw_polygon(gc, rgbFace, zip(thisxverts, thisyverts)) - def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): - raise NotImplementedError +# def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): +# raise NotImplementedError - def draw_text(self, gc, x, y, s, prop, angle, ismath=False): - """ - Draw the text.Text instance s at x,y (display coords) with font - properties instance prop at angle in degrees, using GraphicsContext gc +# def draw_text(self, gc, x, y, s, prop, angle, ismath=False): +# """ +# Draw the text.Text instance s at x,y (display coords) with font +# properties instance prop at angle in degrees, using GraphicsContext gc - **backend implementers note** +# **backend implementers note** - When you are trying to determine if you have gotten your bounding box - right (which is what enables the text layout/alignment to work - properly), it helps to change the line in text.py +# When you are trying to determine if you have gotten your bounding box +# right (which is what enables the text layout/alignment to work +# properly), it helps to change the line in text.py - if 0: bbox_artist(self, renderer) +# if 0: bbox_artist(self, renderer) - to if 1, and then the actual bounding box will be blotted along with - your text. - """ - raise NotImplementedError +# to if 1, and then the actual bounding box will be blotted along with +# your text. +# """ +# raise NotImplementedError def flipy(self): """return true if y small numbers are top for renderer @@ -594,6 +594,7 @@ """ Set the clip path and transformation """ + assert path is None or isinstance(path, transforms.TransformedPath) self._clippath = path def set_dashes(self, dash_offset, dash_list): Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -129,6 +129,13 @@ assert marker_trans.is_affine assert trans.is_affine self._renderer.draw_markers(gc, marker_path, marker_trans.frozen(), path, trans.frozen(), rgbFace) + + def draw_path_collection(self, master_transform, clipbox, clippath, clippath_trans, + paths, transforms, facecolors, edgecolors, linewidths, linestyles, antialiaseds): + assert master_transform.is_affine + self._renderer.draw_path_collection( + master_transform.frozen(), clipbox, clippath, clippath_trans, + paths, transforms, facecolors, edgecolors, linewidths, linestyles, antialiaseds) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/cbook.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -981,12 +981,16 @@ >>> g.joined('a', 'c') True >>> g.joined('a', 'd') - False""" + False + """ def __init__(self, init=[]): mapping = self._mapping = {} for x in init: mapping[x] = [x] - + + def __contains__(self, item): + return item in self._mapping + def join(self, a, *args): """ Join given arguments into the same set. @@ -1019,7 +1023,7 @@ def __iter__(self): """ - Returns an iterator returning each of the disjoint sets as a list. + Returns an iterator yielding each of the disjoint sets as a list. """ seen = set() for elem, group in self._mapping.iteritems(): Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -17,7 +17,11 @@ import matplotlib.artist as artist import matplotlib.backend_bases as backend_bases import matplotlib.nxutils as nxutils +import matplotlib.path as path +# MGDTODO: Now that everything draws through draw_path_collection, +# perhaps more stuff could be moved into the Collection base class +# here class Collection(artist.Artist): """ @@ -616,9 +620,10 @@ linewidths = None, colors = None, antialiaseds = None, - linestyle = 'solid', - offsets = None, - transOffset = None,#transforms.identity_transform(), + linestyles = 'solid', + # MGDTODO: Deal with offsets (mayb +# offsets = None, +# transOffset = None,#transforms.identity_transform(), norm = None, cmap = None, pickradius = 5, @@ -663,13 +668,11 @@ matrix _A is not None (ie a call to set_array has been made), at draw time a call to scalar mappable will be made to set the colors. """ - Collection.__init__(self) cm.ScalarMappable.__init__(self, norm, cmap) if linewidths is None : linewidths = (mpl.rcParams['lines.linewidth'], ) - if colors is None : colors = (mpl.rcParams['lines.color'],) if antialiaseds is None : @@ -678,19 +681,20 @@ self._colors = _colors.colorConverter.to_rgba_list(colors) self._aa = self._get_value(antialiaseds) self._lw = self._get_value(linewidths) - self.set_linestyle(linestyle) + self.set_linestyles(linestyles) self._uniform_offsets = None - if offsets is not None: - offsets = npy.asarray(offsets) - if len(offsets.shape) == 1: - offsets = offsets[npy.newaxis,:] # Make it Nx2. - if transOffset is None: - if offsets is not None: - self._uniform_offsets = offsets - offsets = None - transOffset = transforms.identity_transform() - self._offsets = offsets - self._transOffset = transOffset + # MGDTODO: Deal with offsets +# if offsets is not None: +# offsets = npy.asarray(offsets) +# if len(offsets.shape) == 1: +# offsets = offsets[npy.newaxis,:] # Make it Nx2. +# if transOffset is None: +# if offsets is not None: +# self._uniform_offsets = offsets +# offsets = None +# transOffset = transforms.IdentityTransform() +# self._offsets = offsets +# self._transOffset = transOffset self.set_segments(segments) self.pickradius = pickradius self.update(kwargs) @@ -712,9 +716,10 @@ mx,my = mouseevent.x,mouseevent.y transform = self.get_transform() + # MGDTODO: Numpify ind = [] for this in xrange(len(self._segments)): - xy = transform.seq_xy_tups(self._segments[this]) + xy = transform.transform_point(self._segments[this]) this_ind = ML.segment_hits(mx,my,xy[:,0],xy[:,1],self.pickradius) ind.extend([(this,k) for k in this_ind]) return len(ind)>0,dict(ind=ind) @@ -722,45 +727,47 @@ def set_pickradius(self,pickradius): self.pickradius = 5 def get_pickradius(self): return self.pickradius - def get_transoffset(self): - if self._transOffset is None: - self._transOffset = transforms.identity_transform() - return self._transOffset + # MGDTODO: Support offsets (maybe) +# def get_transoffset(self): +# if self._transOffset is None: +# self._transOffset = transforms.identity_transform() +# return self._transOffset def set_segments(self, segments): if segments is None: return - self._segments = [npy.asarray(seg) for seg in segments] - if self._uniform_offsets is not None: - self._add_offsets() + self._paths = [path.Path(seg, closed=False) for seg in segments] + +# if self._uniform_offsets is not None: +# self._add_offsets() set_verts = set_segments # for compatibility with PolyCollection - def _add_offsets(self): - segs = self._segments - offsets = self._uniform_offsets - Nsegs = len(segs) - Noffs = offsets.shape[0] - if Noffs == 1: - for i in range(Nsegs): - segs[i] = segs[i] + i * offsets - else: - for i in range(Nsegs): - io = i%Noffs - segs[i] = segs[i] + offsets[io:io+1] + # MGDTODO: Support offsets (maybe) +# def _add_offsets(self): +# segs = self._segments +# offsets = self._uniform_offsets +# Nsegs = len(segs) +# Noffs = offsets.shape[0] +# if Noffs == 1: +# for i in range(Nsegs): +# segs[i] = segs[i] + i * offsets +# else: +# for i in range(Nsegs): +# io = i%Noffs +# segs[i] = segs[i] + offsets[io:io+1] - def draw(self, renderer): if not self.get_visible(): return renderer.open_group('linecollection') transform = self.get_transform() - transoffset = self.get_transoffset() + # MGDTODO: Deal with offsets (maybe) +# transoffset = self.get_transoffset() - transform.freeze() - transoffset.freeze() + # segments = self._segments + # MGDTODO: Deal with offsets (maybe) + # offsets = self._offsets - segments = self._segments - offsets = self._offsets - + # MGDTODO: Transform the paths (since we don't keep track of segments anymore if self.have_units(): segments = [] for segment in self._segments: @@ -768,20 +775,18 @@ xs = self.convert_xunits(xs) ys = self.convert_yunits(ys) segments.append(zip(xs, ys)) - if self._offsets is not None: - xs = self.convert_xunits(self._offsets[:0]) - ys = self.convert_yunits(self._offsets[:1]) - offsets = zip(xs, ys) - +# if self._offsets is not None: +# xs = self.convert_xunits(self._offsets[:0]) +# ys = self.convert_yunits(self._offsets[:1]) +# offsets = zip(xs, ys) + self.update_scalarmappable() #print 'calling renderer draw line collection' - renderer.draw_line_collection( - segments, transform, self.clipbox, - self._colors, self._lw, self._ls, self._aa, offsets, - transoffset) - transform.thaw() - transoffset.thaw() - + clippath, clippath_trans = self.get_transformed_clip_path_and_affine() + renderer.draw_path_collection( + transform, self.clipbox, clippath, clippath_trans, + self._paths, [None], [None], + self._colors, self._lw, self._ls, self._aa) renderer.close_group('linecollection') def set_linewidth(self, lw): @@ -795,18 +800,34 @@ self._lw = self._get_value(lw) - def set_linestyle(self, ls): + def set_linestyles(self, ls): """ Set the linestyles(s) for the collection. ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) ] """ - if cbook.is_string_like(ls): - dashes = backend_bases.GraphicsContextBase.dashd[ls] - elif cbook.iterable(ls) and len(ls)==2: - dashes = ls - else: raise ValueError('Do not know how to convert %s to dashes'%ls) + try: + if cbook.is_string_like(ls): + dashes = [backend_bases.GraphicsContextBase.dashd[ls]] + elif cbook.iterable(ls): + try: + dashes = [] + for x in ls: + if cbook.is_string_like(x): + dashes.append(backend_bases.GraphicsContextBase.dashd[ls]) + elif cbook.iterator(x) and len(x) == 2: + dashes.append(x) + else: + raise ValueError() + except ValueError: + if len(ls)==2: + dashes = ls + else: + raise ValueError() + else: + raise ValueError() + except ValueError: + raise ValueError('Do not know how to convert %s to dashes'%ls) - self._ls = dashes def set_color(self, c): @@ -860,23 +881,6 @@ return self._colors get_colors = get_color # for compatibility with old versions - def get_verts(self, dataTrans=None): - '''Return vertices in data coordinates. - The calculation is incomplete in general; it is based - on the segments or the offsets, whichever is using - dataTrans as its transformation, so it does not take - into account the combined effect of segments and offsets. - ''' - verts = [] - if self._offsets is None: - for seg in self._segments: - verts.extend(seg) - return [tuple(xy) for xy in verts] - if self.get_transoffset() == dataTrans: - return [tuple(xy) for xy in self._offsets] - raise NotImplementedError('Vertices in data coordinates are calculated\n' - + 'with offsets only if _transOffset == dataTrans.') - def update_scalarmappable(self): """ If the scalar mappable array is not none, update colors Modified: branches/transforms/lib/matplotlib/image.py =================================================================== --- branches/transforms/lib/matplotlib/image.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/image.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -181,7 +181,6 @@ if not self.get_visible(): return im = self.make_image(renderer.get_image_magnification()) l, b, widthDisplay, heightDisplay = self.axes.bbox.bounds - print self.axes.bbox.frozen() renderer.draw_image(l, b, im, self.axes.bbox.frozen()) def contains(self, mouseevent): Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/legend.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -34,7 +34,7 @@ from patches import Patch, Rectangle, RegularPolygon, Shadow, bbox_artist, draw_bbox from collections import LineCollection, RegularPolyCollection, PatchCollection from text import Text -from transforms import Bbox, BboxTransform +from transforms import Affine2D, Bbox, BboxTransform def line_cuts_bbox(line, bbox): """ Return True if and only if line cuts bbox. """ @@ -164,8 +164,9 @@ else: raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent - self.set_transform( BboxTransform(Bbox.unit(), parent.bbox) ) - + self._offsetTransform = Affine2D() + self.set_transform( self._offsetTransform + BboxTransform(Bbox.unit(), parent.bbox) ) + if loc is None: loc = rcParams["legend.loc"] if not self.isaxes and loc in [0,'best']: @@ -429,24 +430,8 @@ def _offset(self, ox, oy): 'Move all the artists by ox,oy (axes coords)' - for t in self.texts: - x,y = t.get_position() - t.set_position( (x+ox, y+oy) ) + self._offsetTransform.clear().translate(ox, oy) - for h in self.legendHandles: - if isinstance(h, Line2D): - x,y = h.get_xdata(orig=False), h.get_ydata(orig=False) - h.set_data( x+ox, y+oy) - elif isinstance(h, Rectangle): - h.xy[0] = h.xy[0] + ox - h.xy[1] = h.xy[1] + oy - elif isinstance(h, RegularPolygon): - h.verts = [(x + ox, y + oy) for x, y in h.verts] - - x, y = self.legendPatch.get_x(), self.legendPatch.get_y() - self.legendPatch.set_x(x+ox) - self.legendPatch.set_y(y+oy) - def _find_best_position(self, width, height, consider=None): """Determine the best location to place the legend. Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -349,6 +349,7 @@ self._picker = p def get_window_extent(self, renderer): + # MGDTODO: Numpify xy = self.get_transform().transform(self._xy) x = xy[:, 0] @@ -690,7 +691,7 @@ def _draw_pixel(self, renderer, gc, path, path_trans): rgbFace = self._get_rgb_face() transform = Affine2D().translate(-0.5, -0.5) - renderer.draw_markers(gc, Path.unit_rectangle, transform, + renderer.draw_markers(gc, Path.unit_rectangle(), transform, path, path_trans, rgbFace) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/patches.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -221,8 +221,11 @@ path = self.get_path() transform = self.get_transform() - - renderer.draw_path(gc, path, transform, rgbFace) + # MGDTODO: Use a transformed path here? + tpath = transform.transform_path_non_affine(path) + affine = transform.get_affine() + + renderer.draw_path(gc, tpath, affine, rgbFace) #renderer.close_group('patch') @@ -234,8 +237,7 @@ def get_window_extent(self, renderer=None): - return Bbox.from_lbrt( - get_path_extents(self.get_path(), self.get_patch_transform())) + return self.get_path().get_extents(self.get_transform()) def set_lw(self, val): @@ -441,21 +443,49 @@ Valid kwargs are: %(Patch)s """ + self._xy = xy + self._orientation = orientation + self._radius = radius + self._path = Path.unit_regular_polygon(numVertices) + self._poly_transform = transforms.Affine2D() + self._update_transform() + Patch.__init__(self, **kwargs) + + __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - self._path = Path.unit_regular_polygon(numVertices) - self._poly_transform = transforms.Affine2D() \ - .scale(radius) \ - .rotate(orientation) \ - .translate(*xy) + def _update_transform(self): + self._poly_transform.clear() \ + .scale(self.radius) \ + .rotate(self.orientation) \ + .translate(*self.xy) - __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + def _get_xy(self): + return self._xy + def _set_xy(self, xy): + self._xy = xy + self._update_transform() + xy = property(_get_xy, _set_xy) + def _get_orientation(self): + return self._orientation + def _set_orientation(self, xy): + self._orientation = xy + self._update_transform() + orientation = property(_get_orientation, _set_orientation) + + def _get_radius(self): + return self._radius + def _set_radius(self, xy): + self._radius = xy + self._update_transform() + radius = property(_get_radius, _set_radius) + def get_path(self): return self._path def get_patch_transform(self): - return self._poly_transform + return self._poly_transform class Polygon(Patch): """ @@ -492,10 +522,8 @@ """ Patch.__init__(self, **kwargs) - self.theta1 = theta1 - self.theta2 = theta2 self._path = Path.wedge(theta1, theta2) - self._path_transform = transforms.Affine2D() \ + self._patch_transform = transforms.Affine2D() \ .scale(r).translate(*center) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -503,7 +531,7 @@ return self._path def get_patch_transform(self): - return self._path_transform + return self._patch_transform class Arrow(Polygon): """ @@ -515,6 +543,12 @@ cx,cy = (x1+x2)/2.,(y1+y2)/2. return "Arrow(%g,%g)"%(cx,cy) + _path = Path( [ + [ 0.0, 0.1 ], [ 0.0, -0.1], + [ 0.8, -0.1 ], [ 0.8, -0.3], + [ 1.0, 0.0 ], [ 0.8, 0.3], + [ 0.8, 0.1 ] ] ) + def __init__( self, x, y, dx, dy, width=1.0, **kwargs ): """Draws an arrow, starting at (x,y), direction and length given by (dx,dy) the width of the arrow is scaled by width @@ -522,22 +556,23 @@ Valid kwargs are: %(Patch)s """ - # MGDTODO: Implement me - arrow = npy.array( [ - [ 0.0, 0.1 ], [ 0.0, -0.1], - [ 0.8, -0.1 ], [ 0.8, -0.3], - [ 1.0, 0.0 ], [ 0.8, 0.3], - [ 0.8, 0.1 ] ] ) L = npy.sqrt(dx**2+dy**2) or 1 # account for div by zero - arrow[:,0] *= L - arrow[:,1] *= width cx = float(dx)/L sx = float(dy)/L - M = npy.array( [ [ cx, sx],[ -sx, cx ] ] ) - verts = npy.dot( arrow, M )+ [x,y] - Polygon.__init__( self, [ tuple(t) for t in verts ], **kwargs ) + + trans1 = transforms.Affine2D().scale(L, width) + trans2 = transforms.Affine2D.from_values(cx, sx, -sx, cx, 0.0, 0.0) + trans3 = transforms.Affine2d().translate(x, y) + trans = trans1 + trans2 + trans3 + self._patch_transform = trans.frozen() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + def get_path(self): + return self._path + + def get_patch_transform(self): + return self._patch_transform + class FancyArrow(Polygon): """Like Arrow, but lets you set head width and head height independently.""" @@ -614,7 +649,7 @@ Polygon.__init__(self, map(tuple, verts), **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd -class YAArrow(Polygon): +class YAArrow(Patch): """ Yet another arrow class @@ -640,25 +675,25 @@ %(Patch)s """ - # MGDTODO: Implement me self.dpi = dpi self.xytip = xytip self.xybase = xybase self.width = width self.frac = frac self.headwidth = headwidth - verts = self.get_verts() - Polygon.__init__(self, verts, **kwargs) + Patch.__init__(self, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - - - def get_verts(self): + def get_path(self): + # MGDTODO: Since this is dpi dependent, we need to recompute + # the path every time. Perhaps this can be plugged through the + # dpi transform instead (if only we know how to get it...) + # the base vertices x1, y1 = self.xytip x2, y2 = self.xybase - k1 = self.width*self.dpi.get()/72./2. - k2 = self.headwidth*self.dpi.get()/72./2. + k1 = self.width*self.dpi/72./2. + k2 = self.headwidth*self.dpi/72./2. xb1, yb1, xb2, yb2 = self.getpoints(x1, y1, x2, y2, k1) # a point on the segment 20% of the distance from the tip to the base @@ -669,12 +704,14 @@ xc1, yc1, xc2, yc2 = self.getpoints(x1, y1, xm, ym, k1) xd1, yd1, xd2, yd2 = self.getpoints(x1, y1, xm, ym, k2) - xs = self.convert_xunits([xb1, xb2, xc2, xd2, x1, xd1, xc1]) ys = self.convert_yunits([yb1, yb2, yc2, yd2, y1, yd1, yc1]) - return zip(xs, ys) + return Path(zip(xs, ys)) + def get_patch_transform(self): + return transforms.IdentityTransform() + def getpoints(self, x1,y1,x2,y2, k): """ for line segment defined by x1,y1 and x2,y2, return the points on Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-10-05 18:56:10 UTC (rev 3923) +++ branches/transforms/lib/matplotlib/path.py 2007-10-05 19:25:33 UTC (rev 3924) @@ -87,12 +87,14 @@ vertices = ma.asarray(vertices, npy.float_) if codes is None: - if closed: + if len(vertices) == 0: + codes = npy.zeros((0, ), self.code_type) + elif closed: codes = self.LINETO * npy.ones( vertices.shape[0] + 1, self.code_type) codes[0] = self.MOVETO - codes[-1] = self.CLOSEPOLY - vertices = npy.concatenate((vertices, [[0.0, 0.0]])) + codes[-1] = self.LINETO + vertices = npy.concatenate((vertices, [vertices[0]])) else: codes = self.LINETO * npy.ones( vertices.shape[0], self.code_type) @@ -187,9 +189,9 @@ algorithm will take into account the curves and deal with control points appropriately. """ - from transforms import Bbox, IdentityTransform + from transforms import Affine2D, Bbox if transform is None: - transform = IdentityTransform + transform = Affine2D() return Bbox.from_lbrt(*get_path_extents(self, transform)) def i... [truncated message content] |
From: <ds...@us...> - 2007-10-05 18:56:16
|
Revision: 3923 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3923&view=rev Author: dsdale Date: 2007-10-05 11:56:10 -0700 (Fri, 05 Oct 2007) Log Message: ----------- remove generator expressions from texmanager and mpltraits Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/config/mpltraits.py trunk/matplotlib/lib/matplotlib/texmanager.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007-10-05 18:30:39 UTC (rev 3922) +++ trunk/matplotlib/CHANGELOG 2007-10-05 18:56:10 UTC (rev 3923) @@ -1,3 +1,6 @@ +2007-10-05 remove generator expressions from texmanager and mpltraits. + generator expressions are not supported by python-2.3 - DSD + 2007-10-01 Made matplotlib.use() raise an exception if called after backends has been imported. Modified: trunk/matplotlib/lib/matplotlib/config/mpltraits.py =================================================================== --- trunk/matplotlib/lib/matplotlib/config/mpltraits.py 2007-10-05 18:30:39 UTC (rev 3922) +++ trunk/matplotlib/lib/matplotlib/config/mpltraits.py 2007-10-05 18:56:10 UTC (rev 3923) @@ -46,7 +46,7 @@ def info(self): be = self.backends.keys() be.sort - return "one of %s"% ', '.join('%s'%i for i in be) + return "one of %s"% ', '.join(['%s'%i for i in be]) class BoolHandler(T.TraitHandler): @@ -73,7 +73,7 @@ return self.error(object, name, value) def info(self): - return "one of %s"% ', '.join('%s'%i for i in self.bools.keys()) + return "one of %s"% ', '.join(['%s'%i for i in self.bools.keys()]) flexible_true = T.Trait(True, BoolHandler()) flexible_false = T.Trait(False, BoolHandler()) Modified: trunk/matplotlib/lib/matplotlib/texmanager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/texmanager.py 2007-10-05 18:30:39 UTC (rev 3922) +++ trunk/matplotlib/lib/matplotlib/texmanager.py 2007-10-05 18:56:10 UTC (rev 3923) @@ -110,7 +110,7 @@ _rc_cache = None _rc_cache_keys = ('text.latex.preamble', )\ - + tuple('font.'+n for n in ('family', ) + font_families) + + tuple(['font.'+n for n in ('family', ) + font_families]) def __init__(self): @@ -125,7 +125,7 @@ fontconfig = [self.font_family] for font_family, font_family_attr in \ - ((ff, ff.replace('-', '_')) for ff in self.font_families): + [(ff, ff.replace('-', '_')) for ff in self.font_families]: for font in rcParams['font.'+font_family]: if font.lower() in self.font_info: found_font = self.font_info[font.lower()] @@ -163,7 +163,7 @@ def get_font_config(self): "Reinitializes self if rcParams self depends on have changed." if self._rc_cache is None: - self._rc_cache = dict((k,None) for k in self._rc_cache_keys) + self._rc_cache = dict([(k,None) for k in self._rc_cache_keys]) changed = [par for par in self._rc_cache_keys if rcParams[par] != \ self._rc_cache[par]] if changed: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2007-10-05 18:30:42
|
Revision: 3922 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3922&view=rev Author: jswhit Date: 2007-10-05 11:30:39 -0700 (Fri, 05 Oct 2007) Log Message: ----------- fix typo. Modified Paths: -------------- trunk/toolkits/basemap/MANIFEST.in Modified: trunk/toolkits/basemap/MANIFEST.in =================================================================== --- trunk/toolkits/basemap/MANIFEST.in 2007-10-05 17:01:36 UTC (rev 3921) +++ trunk/toolkits/basemap/MANIFEST.in 2007-10-05 18:30:39 UTC (rev 3922) @@ -57,7 +57,7 @@ include examples/tissot.dbf include examples/tissot.shp include examples/tissot.shx -include examples/show_colorbar.py +include examples/show_colormaps.py include examples/plotprecip.py include examples/nws_precip_conus_20061222.nc include examples/README This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jr...@us...> - 2007-10-05 17:01:39
|
Revision: 3921 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3921&view=rev Author: jrevans Date: 2007-10-05 10:01:36 -0700 (Fri, 05 Oct 2007) Log Message: ----------- Fixed an error in calculating the mid-point of a bar since the values are now lists and not arrays, they need to be iterated to perform the arithmetic. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-10-05 16:29:17 UTC (rev 3920) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-10-05 17:01:36 UTC (rev 3921) @@ -3262,9 +3262,9 @@ pass elif align == 'center': if orientation == 'vertical': - left = left - width/2. + left = [left[i] - width[i]/2. for i in range(len(left))] elif orientation == 'horizontal': - bottom = bottom-height/2. + bottom = [bottom[i] - height[i]/2. for i in range(len(bottom))] else: raise ValueError, 'invalid alignment: %s' % align This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jr...@us...> - 2007-10-05 16:29:19
|
Revision: 3920 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3920&view=rev Author: jrevans Date: 2007-10-05 09:29:17 -0700 (Fri, 05 Oct 2007) Log Message: ----------- Fixed a typo in the Ellipse code that was causing the ellipse angle to go in the wrong direction. When I was testing I had forgotten to turn off the axes inversion test. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2007-10-05 06:58:15 UTC (rev 3919) +++ trunk/matplotlib/lib/matplotlib/patches.py 2007-10-05 16:29:17 UTC (rev 3920) @@ -815,8 +815,8 @@ angle = self.angle * math.pi / 180.0 # convert the angle to polar coordinates (Assume r = 1.0) - anglex = math.cos(-angle) - angley = math.sin(-angle) + anglex = math.cos(angle) + angley = math.sin(angle) # transform the angle vertex and the origin angle_verts = npy.array(((anglex, angley), (0.0, 0.0)), npy.float) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2007-10-05 06:58:21
|
Revision: 3919 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3919&view=rev Author: efiring Date: 2007-10-04 23:58:15 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Tweaked automatic contour level calculation Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/contour.py trunk/matplotlib/lib/matplotlib/ticker.py Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2007-10-05 06:18:25 UTC (rev 3918) +++ trunk/matplotlib/lib/matplotlib/contour.py 2007-10-05 06:58:15 UTC (rev 3919) @@ -492,16 +492,18 @@ one contour line, but two filled regions, and therefore three levels to provide boundaries for both regions. ''' + if self.locator is None: + self.locator = ticker.MaxNLocator(N+1) + locator = self.locator zmax = self.zmax zmin = self.zmin - zmargin = (zmax - zmin) * 0.001 # so z < (zmax + zmargin) - zmax = zmax + zmargin - intv = transforms.Interval(transforms.Value(zmin), transforms.Value(zmax)) - if self.locator is None: - self.locator = ticker.MaxNLocator(N+1) - self.locator.set_view_interval(intv) - self.locator.set_data_interval(intv) - lev = self.locator() + locator.set_bounds(zmin, zmax) + lev = locator() + zmargin = (zmax - zmin) * 0.000001 # so z < (zmax + zmargin) + if zmax >= lev[-1]: + lev[-1] += zmargin + if zmin <= lev[0]: + lev[0] -= zmargin self._auto = True if self.filled: return lev Modified: trunk/matplotlib/lib/matplotlib/ticker.py =================================================================== --- trunk/matplotlib/lib/matplotlib/ticker.py 2007-10-05 06:18:25 UTC (rev 3918) +++ trunk/matplotlib/lib/matplotlib/ticker.py 2007-10-05 06:58:15 UTC (rev 3919) @@ -402,7 +402,7 @@ self.format = '$%s$' % self.format elif self._useMathText: self.format = '$\mathdefault{%s}$' % self.format - + def pprint_val(self, x): xp = (x-self.offset)/10**self.orderOfMagnitude if npy.absolute(xp) < 1e-8: xp = 0 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2007-10-05 06:18:27
|
Revision: 3918 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3918&view=rev Author: efiring Date: 2007-10-04 23:18:25 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Minor cleanup of arguments and docstring in contour Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/contour.py Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2007-10-04 22:13:18 UTC (rev 3917) +++ trunk/matplotlib/lib/matplotlib/contour.py 2007-10-05 06:18:25 UTC (rev 3918) @@ -397,12 +397,7 @@ cmap = kwargs.get('cmap', None) self.colors = kwargs.get('colors', None) norm = kwargs.get('norm', None) - self.clip_ends = kwargs.get('clip_ends', None) ######## self.extend = kwargs.get('extend', 'neither') - if self.clip_ends is not None: - warnings.warn("'clip_ends' has been replaced by 'extend'") - self.levels = self.levels[1:-1] # discard specified end levels - self.extend = 'both' # regenerate end levels self.antialiased = kwargs.get('antialiased', True) self.nchunk = kwargs.get('nchunk', 0) self.locator = kwargs.get('locator', None) @@ -436,10 +431,8 @@ _mask = None if self.filled: - if self.linewidths is None: - self.linewidths = 0.05 # Good default for Postscript. - if cbook.iterable(self.linewidths): - self.linewidths = self.linewidths[0] + if self.linewidths is not None: + warnings.warn('linewidths is ignored by contourf') C = _cntr.Cntr(x, y, z.filled(), _mask) lowers = self._levels[:-1] uppers = self._levels[1:] @@ -447,7 +440,6 @@ nlist = C.trace(level, level_upper, points = 0, nchunk = self.nchunk) col = collections.PolyCollection(nlist, - linewidths = (self.linewidths,), antialiaseds = (self.antialiased,), edgecolors= 'None') self.ax.add_collection(col) @@ -627,16 +619,16 @@ self._levels = npy.asarray(self._levels) self.vmin = npy.amin(self.levels) # alternative would be self.layers self.vmax = npy.amax(self.levels) - if self.extend in ('both', 'min') or self.clip_ends: + if self.extend in ('both', 'min'): self.vmin = 2 * self.levels[0] - self.levels[1] - if self.extend in ('both', 'max') or self.clip_ends: + if self.extend in ('both', 'max'): self.vmax = 2 * self.levels[-1] - self.levels[-2] self.layers = self._levels # contour: a line is a thin layer if self.filled: self.layers = 0.5 * (self._levels[:-1] + self._levels[1:]) - if self.extend in ('both', 'min') or self.clip_ends: + if self.extend in ('both', 'min'): self.layers[0] = 0.5 * (self.vmin + self._levels[1]) - if self.extend in ('both', 'max') or self.clip_ends: + if self.extend in ('both', 'max'): self.layers[-1] = 0.5 * (self.vmax + self._levels[-2]) return (x, y, z) @@ -774,7 +766,6 @@ contour levels if they are not given explicitly via the V argument. - ***** New: ***** * extend = 'neither', 'both', 'min', 'max' Unless this is 'neither' (default), contour levels are automatically added to one or both ends of the range so that @@ -782,8 +773,7 @@ mapped to the special colormap values which default to the ends of the colormap range, but can be set via Colormap.set_under() and Colormap.set_over() methods. - To replace clip_ends=True and V = [-100, 2, 1, 0, 1, 2, 100], - use extend='both' and V = [2, 1, 0, 1, 2]. + **************** contour only: @@ -799,29 +789,13 @@ matplotlibrc is used contourf only: - ***** Obsolete: **** - * 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. - * linewidths = None or a number; default of 0.05 works for - Postscript; a value of about 0.5 seems better for Agg. - * antialiased = True (default) or False; if False, there is - no need to increase the linewidths for Agg, but True gives - nicer color boundaries. If antialiased is True and linewidths - is too small, then there may be light-colored lines at the - color boundaries caused by the antialiasing. + * antialiased = True (default) or False * nchunk = 0 (default) for no subdivision of the domain; specify a positive integer to divide the domain into subdomains of roughly nchunk by nchunk points. This may never actually be advantageous, so this option may be removed. Chunking introduces artifacts at the chunk - boundaries unless antialiased = False, or linewidths is - set to a large enough value for the particular renderer and - resolution. + boundaries unless antialiased = False """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jr...@us...> - 2007-10-04 22:13:20
|
Revision: 3917 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3917&view=rev Author: jrevans Date: 2007-10-04 15:13:18 -0700 (Thu, 04 Oct 2007) Log Message: ----------- axes.py: Reverted get/set xlim/ylim methods to original state Added get/set xbound/ybound to handle axes inversion maintenance Removed inverted axes flags patches.py: Added some logic to xform an Ellipse angle as per the Ellipse's transformation. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/patches.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-10-04 21:39:07 UTC (rev 3916) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-10-04 22:13:18 UTC (rev 3917) @@ -503,9 +503,6 @@ self.set_label(label) self.set_figure(fig) - self._invertedx = False - self._invertedy = False - # this call may differ for non-sep axes, eg polar self._init_axis() @@ -896,9 +893,9 @@ figW,figH = self.get_figure().get_size_inches() fig_aspect = figH/figW #print 'figW, figH, fig_aspect', figW, figH, fig_aspect - xmin,xmax = self.get_xlim() + xmin,xmax = self.get_xbound() xsize = max(math.fabs(xmax-xmin), 1e-30) - ymin,ymax = self.get_ylim() + ymin,ymax = self.get_ybound() ysize = max(math.fabs(ymax-ymin), 1e-30) if self._adjustable == 'box': if data_ratio is None: @@ -956,14 +953,14 @@ yc = 0.5*(ymin+ymax) y0 = yc - Ysize/2.0 y1 = yc + Ysize/2.0 - self.set_ylim((y0, y1)) + self.set_ybound((y0, y1)) #print 'New y0, y1:', y0, y1 #print 'New ysize, ysize/xsize', y1-y0, (y1-y0)/xsize else: xc = 0.5*(xmin+xmax) x0 = xc - Xsize/2.0 x1 = xc + Xsize/2.0 - self.set_xlim((x0, x1)) + self.set_xbound((x0, x1)) #print 'New x0, x1:', x0, x1 #print 'New xsize, ysize/xsize', x1-x0, ysize/(x1-x0) @@ -1257,24 +1254,20 @@ len(self.lines)==0 and len(self.patches)==0)): - if scalex: self.set_xlim(self.dataLim.intervalx().get_bounds()) + if scalex: self.set_xbound(self.dataLim.intervalx().get_bounds()) - if scaley: self.set_ylim(self.dataLim.intervaly().get_bounds()) + if scaley: self.set_ybound(self.dataLim.intervaly().get_bounds()) return if scalex: - xl = self.get_xlim() + xl = self.get_xbound() XL = self.xaxis.get_major_locator().autoscale() - if xl[1] < xl[0]: - XL = XL[::-1] - self.set_xlim(XL) + self.set_xbound(XL) if scaley: ylocator = self.yaxis.get_major_locator() - yl = self.get_ylim() + yl = self.get_ybound() YL = ylocator.autoscale() - if yl[1] < yl[0]: - YL = YL[::-1] - self.set_ylim(YL) + self.set_ybound(YL) #### Drawing def draw(self, renderer=None, inframe=False): @@ -1503,26 +1496,52 @@ ### data limits, ticks, tick labels, and formatting - def invert_xaxis(self, invert=True): - "Invert the x-axis if 'invert' is True." - self._invertedx = invert + def invert_xaxis(self): + "Invert the x-axis." + left, right = self.get_xlim() + self.set_xlim(right, left) def xaxis_inverted(self): 'Returns True if the x-axis is inverted.' - return self._invertedx + left, right = self.get_xlim() + return right < left - def get_xlim(self): - """Get the x-axis range [xmin, xmax] + def get_xbound(self): + "Returns the x-axis numerical bounds in the form of lowerBound < upperBound" + left, right = self.get_xlim() + if left < right: + return left, right + else: + return right, left - NOTE: The returned values are always [xmin, xmax] such that - xmin < xmax; regardless of whether or not the axes are inverted. + def set_xbound(self, lower=None, upper=None): + """Set the lower and upper numerical bounds of the x-axis. + This method will honor axes inversion regardless of parameter order. """ - bound1, bound2 = self.viewLim.intervalx().get_bounds() - if ( self._invertedx ): - return bound2, bound1 + if upper is None and iterable(lower): + lower,upper = lower + + old_lower,old_upper = self.get_xbound() + + if lower is None: lower = old_lower + if upper is None: upper = old_upper + + if self.xaxis_inverted(): + if lower < upper: + self.set_xlim(upper, lower) + else: + self.set_xlim(lower, upper) else: - return bound1, bound2 + if lower < upper: + self.set_xlim(lower, upper) + else: + self.set_xlim(upper, lower) + def get_xlim(self): + 'Get the x axis range [xmin, xmax]' + return self.viewLim.intervalx().get_bounds() + + def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs): """ set_xlim(self, *args, **kwargs): @@ -1560,25 +1579,12 @@ if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax - # provided for backwards compatability - if ( xmax < xmin ): - # swap the values so that xmin < xmax and set inverted flag - tmp = xmin - xmin = xmax - xmax = tmp - self.invert_xaxis( True ) - if (self.transData.get_funcx().get_type()==mtrans.LOG10 and min(xmin, xmax)<=0): raise ValueError('Cannot set nonpositive limits with log transform') - if ( self._invertedx ): - xmax, xmin = mtrans.nonsingular(xmax, xmin, increasing=False) - self.viewLim.intervalx().set_bounds(xmax, xmin) - else: - xmin, xmax = mtrans.nonsingular(xmin, xmax, increasing=False) - self.viewLim.intervalx().set_bounds(xmin, xmax) - + xmin, xmax = mtrans.nonsingular(xmin, xmax, increasing=False) + self.viewLim.intervalx().set_bounds(xmin, xmax) if emit: self.callbacks.process('xlim_changed', self) return xmin, xmax @@ -1654,26 +1660,51 @@ return self.xaxis.set_ticklabels(labels, fontdict, **kwargs) set_xticklabels.__doc__ = cbook.dedent(set_xticklabels.__doc__) % martist.kwdocd - def invert_yaxis(self, invert=True): - "Invert the y-axis if 'invert' is True." - self._invertedy = invert + def invert_yaxis(self): + "Invert the y-axis." + left, right = self.get_ylim() + self.set_ylim(right, left) def yaxis_inverted(self): 'Returns True if the y-axis is inverted.' - return self._invertedy + left, right = self.get_ylim() + return right < left - def get_ylim(self): - """Get the y-axis range [xmin, xmax] + def get_ybound(self): + "Returns the y-axis numerical bounds in the form of lowerBound < upperBound" + left, right = self.get_ylim() + if left < right: + return left, right + else: + return right, left - NOTE: The returned values are always [ymin, ymax] such that - ymin < ymax; regardless of whether or not the axes are inverted. + def set_ybound(self, lower=None, upper=None): + """Set the lower and upper numerical bounds of the y-axis. + This method will honor axes inversion regardless of parameter order. """ - bound1, bound2 = self.viewLim.intervaly().get_bounds() - if ( self._invertedy ): - return bound2, bound1 + if upper is None and iterable(lower): + lower,upper = lower + + old_lower,old_upper = self.get_ybound() + + if lower is None: lower = old_lower + if upper is None: upper = old_upper + + if self.yaxis_inverted(): + if lower < upper: + self.set_ylim(upper, lower) + else: + self.set_ylim(lower, upper) else: - return bound1, bound2 + if lower < upper: + self.set_ylim(lower, upper) + else: + self.set_ylim(upper, lower) + def get_ylim(self): + 'Get the y axis range [ymin, ymax]' + return self.viewLim.intervaly().get_bounds() + def set_ylim(self, ymin=None, ymax=None, emit=True, **kwargs): """ set_ylim(self, *args, **kwargs): @@ -1696,6 +1727,7 @@ ACCEPTS: len(2) sequence of floats """ + if ymax is None and iterable(ymin): ymin,ymax = ymin @@ -1709,25 +1741,12 @@ if ymin is None: ymin = old_ymin if ymax is None: ymax = old_ymax - # provided for backwards compatability - if ( ymax < ymin ): - # swap the values so that ymin < ymax and set inverted flag - tmp = ymin - ymin = ymax - ymax = tmp - self.invert_yaxis( True ) - if (self.transData.get_funcy().get_type()==mtrans.LOG10 and min(ymin, ymax)<=0): raise ValueError('Cannot set nonpositive limits with log transform') - if ( self._invertedy ): - ymax, ymin = mtrans.nonsingular(ymax, ymin, increasing=False) - self.viewLim.intervaly().set_bounds(ymax, ymin) - else: - ymin, ymax = mtrans.nonsingular(ymin, ymax, increasing=False) - self.viewLim.intervaly().set_bounds(ymin, ymax) - + ymin, ymax = mtrans.nonsingular(ymin, ymax, increasing=False) + self.viewLim.intervaly().set_bounds(ymin, ymax) if emit: self.callbacks.process('ylim_changed', self) return ymin, ymax Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2007-10-04 21:39:07 UTC (rev 3916) +++ trunk/matplotlib/lib/matplotlib/patches.py 2007-10-04 22:13:18 UTC (rev 3917) @@ -782,7 +782,8 @@ b,t = y-self.height/2.0, y+self.height/2.0 x,l,r = self.convert_xunits((x,l,r)) y,b,t = self.convert_yunits((y,b,t)) - return npy.array(((x,y),(l,y),(x,t),(r,y),(x,b)), npy.float) + verts = ((x,y), (l,y), (x,t), (r,y), (x,b)) + return npy.array(verts, npy.float) def draw(self, renderer): if not self.get_visible(): return @@ -804,11 +805,32 @@ tverts = self.get_transform().seq_xy_tups(self.get_verts()) # center is first vert - width = tverts[3,0] - tverts[1,0] - height = tverts[2,1] - tverts[4,1] + # take the abs since we do not want a negative width or height as this + # will cause the renderer to misbehave. + width = abs(tverts[3,0] - tverts[1,0]) + height = abs(tverts[2,1] - tverts[4,1]) + # we also have to transform the angle, do this using polar coordinates + # convert to radians + angle = self.angle * math.pi / 180.0 + + # convert the angle to polar coordinates (Assume r = 1.0) + anglex = math.cos(-angle) + angley = math.sin(-angle) + + # transform the angle vertex and the origin + angle_verts = npy.array(((anglex, angley), (0.0, 0.0)), npy.float) + angle_verts = self.get_transform().seq_xy_tups(angle_verts) + + # get the new x and y coords (from the origin) + anglex = angle_verts[0, 0] - angle_verts[1, 0] + angley = angle_verts[0, 1] - angle_verts[1, 1] + + # convert back to an angle (in degrees) + angle = math.atan2(angley, anglex) * 180.0 / math.pi + renderer.draw_arc(gc, rgbFace, tverts[0,0], tverts[0,1], - width, height, 0.0, 360.0, self.angle) + width, height, 0.0, 360.0, angle) class Circle(Ellipse): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sa...@us...> - 2007-10-04 21:39:15
|
Revision: 3916 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3916&view=rev Author: sameerd Date: 2007-10-04 14:39:07 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Fix for "NameError: global name 'ones' is not defined" Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-10-04 19:12:20 UTC (rev 3915) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-10-04 21:39:07 UTC (rev 3916) @@ -2401,9 +2401,9 @@ y = npy.asarray(y) if len(xmin)==1: - xmin = xmin*ones(y.shape, y.dtype) + xmin = xmin*npy.ones(y.shape, y.dtype) if len(xmax)==1: - xmax = xmax*ones(y.shape, y.dtype) + xmax = xmax*npy.ones(y.shape, y.dtype) xmin = npy.asarray(xmin) xmax = npy.asarray(xmax) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-04 19:12:31
|
Revision: 3915 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3915&view=rev Author: mdboom Date: 2007-10-04 12:12:20 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Merged from trunk (a somewhat hairy manual merge this time). Fixed bug (on this branch only) where inverted axes were broken. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/backends/backend_qt.py branches/transforms/lib/matplotlib/backends/backend_qt4.py branches/transforms/lib/matplotlib/backends/backend_qt4agg.py branches/transforms/lib/matplotlib/backends/backend_qtagg.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -498,6 +498,9 @@ self.set_label(label) self.set_figure(fig) + self._invertedx = False + self._invertedy = False + # this call may differ for non-sep axes, eg polar self._init_axis() @@ -1422,10 +1425,25 @@ ### data limits, ticks, tick labels, and formatting + def invert_xaxis(self, invert=True): + "Invert the x-axis if 'invert' is True." + self._invertedx = invert + + def xaxis_inverted(self): + 'Returns True if the x-axis is inverted.' + return self._invertedx + def get_xlim(self): - 'Get the x axis range [xmin, xmax]' - return self.viewLim.intervalx + """Get the x-axis range [xmin, xmax] + NOTE: The returned values are always [xmin, xmax] such that + xmin < xmax; regardless of whether or not the axes are inverted. + """ + bound1, bound2 = self.viewLim.intervalx + if ( self._invertedx ): + return bound2, bound1 + else: + return bound1, bound2 def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs): """ @@ -1463,9 +1481,21 @@ if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax - xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False) + # provided for backwards compatability + if ( xmax < xmin ): + # swap the values so that xmin < xmax and set inverted flag + tmp = xmin + xmin = xmax + xmax = tmp + self.invert_xaxis( True ) - self.viewLim.intervalx = (xmin, xmax) + if ( self._invertedx ): + xmax, xmin = mtransforms.nonsingular(xmax, xmin, increasing=False) + self.viewLim.intervalx = (xmax, xmin) + else: + xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False) + self.viewLim.intervalx = (xmin, xmax) + if emit: self.callbacks.process('xlim_changed', self) # Call all of the other x-axes that are shared with this one @@ -1534,10 +1564,26 @@ return self.xaxis.set_ticklabels(labels, fontdict, **kwargs) set_xticklabels.__doc__ = cbook.dedent(set_xticklabels.__doc__) % martist.kwdocd + def invert_yaxis(self, invert=True): + "Invert the y-axis if 'invert' is True." + self._invertedy = invert + + def yaxis_inverted(self): + 'Returns True if the y-axis is inverted.' + return self._invertedy + def get_ylim(self): - 'Get the y axis range [ymin, ymax]' - return self.viewLim.intervaly + """Get the y-axis range [xmin, xmax] + NOTE: The returned values are always [ymin, ymax] such that + ymin < ymax; regardless of whether or not the axes are inverted. + """ + bound1, bound2 = self.viewLim.intervaly + if ( self._invertedy ): + return bound2, bound1 + else: + return bound1, bound2 + def set_ylim(self, ymin=None, ymax=None, emit=True, **kwargs): """ set_ylim(self, *args, **kwargs): @@ -1572,8 +1618,21 @@ if ymin is None: ymin = old_ymin if ymax is None: ymax = old_ymax - ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) - self.viewLim.intervaly = (ymin, ymax) + # provided for backwards compatability + if ( ymax < ymin ): + # swap the values so that ymin < ymax and set inverted flag + tmp = ymin + ymin = ymax + ymax = tmp + self.invert_yaxis( True ) + + if ( self._invertedy ): + ymax, ymin = mtransforms.nonsingular(ymax, ymin, increasing=False) + self.viewLim.intervaly = (ymax, ymin) + else: + ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) + self.viewLim.intervaly = (ymin, ymax) + if emit: self.callbacks.process('ylim_changed', self) # Call all of the other y-axes that are shared with this one @@ -1582,7 +1641,6 @@ other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False) self.figure.canvas.draw_idle() - return ymin, ymax def get_yscale(self): Modified: branches/transforms/lib/matplotlib/backends/backend_qt.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_qt.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/backends/backend_qt.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -136,10 +136,35 @@ def resizeEvent( self, event ): if DEBUG: print 'resize (%d x %d)' % (event.size().width(), event.size().height()) qt.QWidget.resizeEvent( self, event ) + w = event.size().width() + h = event.size().height() + if DEBUG: print "FigureCanvasQt.resizeEvent(", w, ",", h, ")" + dpival = self.figure.dpi.get() + winch = w/dpival + hinch = h/dpival + self.figure.set_size_inches( winch, hinch ) + self.draw() def resize( self, w, h ): + # Pass through to Qt to resize the widget. qt.QWidget.resize( self, w, h ) + # Resize the figure by converting pixels to inches. + pixelPerInch = self.figure.dpi.get() + wInch = w / pixelPerInch + hInch = h / pixelPerInch + self.figure.set_size_inches( wInch, hInch ) + + # Redraw everything. + self.draw() + + def sizeHint( self ): + w, h = self.get_width_height() + return qt.QSize( w, h ) + + def minumumSizeHint( self ): + return qt.QSize( 10, 10 ) + def _get_key( self, event ): if event.key() < 256: key = event.text().latin1() Modified: branches/transforms/lib/matplotlib/backends/backend_qt4.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_qt4.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/backends/backend_qt4.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -135,10 +135,35 @@ def resizeEvent( self, event ): if DEBUG: print 'resize (%d x %d)' % (event.size().width(), event.size().height()) QtGui.QWidget.resizeEvent( self, event ) + w = event.size().width() + h = event.size().height() + if DEBUG: print "FigureCanvasQtAgg.resizeEvent(", w, ",", h, ")" + dpival = self.figure.dpi.get() + winch = w/dpival + hinch = h/dpival + self.figure.set_size_inches( winch, hinch ) + self.draw() def resize( self, w, h ): + # Pass through to Qt to resize the widget. QtGui.QWidget.resize( self, w, h ) + # Resize the figure by converting pixels to inches. + pixelPerInch = self.figure.dpi.get() + wInch = w / pixelPerInch + hInch = h / pixelPerInch + self.figure.set_size_inches( wInch, hInch ) + + # Redraw everything. + self.draw() + + def sizeHint( self ): + w, h = self.get_width_height() + return QtCore.QSize( w, h ) + + def minumumSizeHint( self ): + return QtCore.QSize( 10, 10 ) + def _get_key( self, event ): if event.key() < 256: key = str(event.text()) Modified: branches/transforms/lib/matplotlib/backends/backend_qt4agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_qt4agg.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/backends/backend_qt4agg.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -65,14 +65,6 @@ def resizeEvent( self, e ): FigureCanvasQT.resizeEvent( self, e ) - w = e.size().width() - h = e.size().height() - if DEBUG: print "FigureCanvasQtAgg.resizeEvent(", w, ",", h, ")" - dpival = self.figure.dpi.get() - winch = w/dpival - hinch = h/dpival - self.figure.set_size_inches( winch, hinch ) - self.draw() def drawRectangle( self, rect ): self.rect = rect Modified: branches/transforms/lib/matplotlib/backends/backend_qtagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_qtagg.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/backends/backend_qtagg.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -64,14 +64,6 @@ def resizeEvent( self, e ): FigureCanvasQT.resizeEvent( self, e ) - w = e.size().width() - h = e.size().height() - if DEBUG: print "FigureCanvasQtAgg.resizeEvent(", w, ",", h, ")" - dpival = self.figure.dpi.get() - winch = w/dpival - hinch = h/dpival - self.figure.set_size_inches( winch, hinch ) - self.draw() def drawRectangle( self, rect ): self.rect = rect Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-04 18:57:27 UTC (rev 3914) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-04 19:12:20 UTC (rev 3915) @@ -1951,10 +1951,12 @@ # MGDTODO: Optimize (perhaps in an extension) def interval_contains(interval, val): - return interval[0] <= val and interval[1] >= val + return ((interval[0] <= val and interval[1] >= val) or + (interval[1] <= val and interval[0] >= val)) def interval_contains_open(interval, val): - return interval[0] < val and interval[1] > val + return ((interval[0] < val and interval[1] > val) or + (interval[1] < val and interval[0] > val)) if __name__ == '__main__': import copy This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-04 18:57:29
|
Revision: 3914 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3914&view=rev Author: mdboom Date: 2007-10-04 11:57:27 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Progress on agg_buffer_to_array example. Modified Paths: -------------- branches/transforms/examples/agg_buffer_to_array.py branches/transforms/examples/polar_demo.py branches/transforms/lib/matplotlib/artist.py branches/transforms/lib/matplotlib/image.py branches/transforms/lib/matplotlib/projections/polar.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/examples/agg_buffer_to_array.py =================================================================== --- branches/transforms/examples/agg_buffer_to_array.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/examples/agg_buffer_to_array.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -12,7 +12,7 @@ # grab rhe pixel buffer and dumpy it into a numpy array buf = fig.canvas.buffer_rgba(0,0) -l, b, w, h = fig.bbox.get_bounds() +l, b, w, h = fig.bbox.bounds X = npy.fromstring(buf, npy.uint8) X.shape = h,w,4 Modified: branches/transforms/examples/polar_demo.py =================================================================== --- branches/transforms/examples/polar_demo.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/examples/polar_demo.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -40,7 +40,7 @@ # information on how to customize the grid locations and labels import numpy as npy -from matplotlib.pyplot import figure, show, rc +from matplotlib.pyplot import figure, show, rc, grid # radar green, solid grid lines rc('grid', color='#316931', linewidth=1, linestyle='-') @@ -55,6 +55,7 @@ theta = 2*npy.pi*r ax.plot(theta, r, color='#ee8d18', lw=3) ax.set_rmax(2.0) +grid(True) ax.set_title("And there was much rejoicing!", fontsize=20) show() Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/lib/matplotlib/artist.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -350,7 +350,7 @@ def _set_gc_clip(self, gc): 'set the clip properly for the gc' if self.clipbox is not None: - gc.set_clip_rectangle(self.clipbox.bounds) + gc.set_clip_rectangle(self.clipbox) gc.set_clip_path(self._clippath) def draw(self, renderer, *args, **kwargs): Modified: branches/transforms/lib/matplotlib/image.py =================================================================== --- branches/transforms/lib/matplotlib/image.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/lib/matplotlib/image.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -149,17 +149,17 @@ dyintv = ymax-ymin # the viewport scale factor - sx = dxintv/self.axes.viewLim.width() - sy = dyintv/self.axes.viewLim.height() + sx = dxintv/self.axes.viewLim.width + sy = dyintv/self.axes.viewLim.height if im.get_interpolation()!=_image.NEAREST: im.apply_translation(-1, -1) # the viewport translation - tx = (xmin-self.axes.viewLim.xmin())/dxintv * numcols - ty = (ymin-self.axes.viewLim.ymin())/dyintv * numrows + tx = (xmin-self.axes.viewLim.xmin)/dxintv * numcols + ty = (ymin-self.axes.viewLim.ymin)/dyintv * numrows - l, b, widthDisplay, heightDisplay = self.axes.bbox.get_bounds() + l, b, widthDisplay, heightDisplay = self.axes.bbox.bounds widthDisplay *= magnification heightDisplay *= magnification @@ -180,8 +180,9 @@ def draw(self, renderer, *args, **kwargs): if not self.get_visible(): return im = self.make_image(renderer.get_image_magnification()) - l, b, widthDisplay, heightDisplay = self.axes.bbox.get_bounds() - renderer.draw_image(l, b, im, self.axes.bbox) + l, b, widthDisplay, heightDisplay = self.axes.bbox.bounds + print self.axes.bbox.frozen() + renderer.draw_image(l, b, im, self.axes.bbox.frozen()) def contains(self, mouseevent): """Test whether the mouse event occured within the image. Modified: branches/transforms/lib/matplotlib/projections/polar.py =================================================================== --- branches/transforms/lib/matplotlib/projections/polar.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/lib/matplotlib/projections/polar.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -12,14 +12,29 @@ IdentityTransform, Transform, TransformWrapper class PolarAxes(Axes): + """ + A polar graph projection, where the input dimensions are theta, r. + + Theta starts pointing east and goes anti-clockwise. + """ name = 'polar' class PolarTransform(Transform): + """ + The base polar transform. This handles projection theta and r into + Cartesian coordinate space, but does not perform the ultimate affine + transformation into the correct position. + """ input_dims = 2 output_dims = 2 is_separable = False def __init__(self, resolution): + """ + Create a new polar transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved polar space. + """ Transform.__init__(self) self._resolution = resolution @@ -32,19 +47,34 @@ x += r * npy.cos(t) y += r * npy.sin(t) return xy + transform.__doc__ = Transform.transform.__doc__ + transform_non_affine = transform + transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ def transform_path(self, path): if len(path.vertices) == 2: path = path.interpolated(self._resolution) return Path(self.transform(path.vertices), path.codes) + transform_path.__doc__ = Transform.transform_path.__doc__ + transform_path_non_affine = transform_path + transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ def inverted(self): return PolarAxes.InvertedPolarTransform() + inverted.__doc__ = Transform.inverted.__doc__ class PolarAffine(Affine2DBase): + """ + The affine part of the polar projection. Scales the output so + that maximum radius rests on the edge of the axes circle. + """ def __init__(self, limits): + """ + limits is the view limit of the data. The only part of + its bounds that is used is ymax (for the radius maximum). + """ Affine2DBase.__init__(self) self._limits = limits self.set_children(limits) @@ -60,8 +90,13 @@ self._inverted = None self._invalid = 0 return self._mtx + get_matrix.__doc__ = Affine2DBase.get_matrix.__doc__ class InvertedPolarTransform(Transform): + """ + The inverse of the polar transform, mapping Cartesian + coordinate space back to t and r. + """ input_dims = 2 output_dims = 2 is_separable = False @@ -73,21 +108,34 @@ theta = npy.arccos(x / r) theta = npy.where(y < 0, 2 * npy.pi - theta, theta) return npy.concatenate((theta, r), 1) + transform.__doc__ = Transform.transform.__doc__ def inverted(self): return PolarAxes.PolarTransform() + inverted.__doc__ = Transform.inverted.__doc__ class ThetaFormatter(Formatter): + """ + Used to format the theta tick labels. Converts the native + unit of radians into degrees and adds a degree symbol. + """ def __call__(self, x, pos=None): + # \u00b0 : degree symbol return u"%d\u00b0" % ((x / npy.pi) * 180.0) class RadialLocator(Locator): + """ + Used to locate radius ticks. + + Ensures that all ticks are strictly positive. For all other + tasks, it delegates to the base Locator (which may be + different depending on the scale of the r-axis. + """ def __init__(self, base): self.base = base def __call__(self): ticks = self.base() - # MGDTODO: Use numpy return [x for x in ticks if x > 0] def autoscale(self): @@ -105,6 +153,10 @@ RESOLUTION = 100 def __init__(self, *args, **kwargs): + """ + Create a new Polar Axes for a polar plot. + """ + self._rpad = 0.05 Axes.__init__(self, *args, **kwargs) self.set_aspect('equal', adjustable='box', anchor='C') @@ -120,7 +172,7 @@ self.yaxis.set_major_locator(self.RadialLocator(self.yaxis.get_major_locator())) def _set_lim_and_transforms(self): - self.dataLim = Bbox([[0.0, 0.0], [npy.pi * 2.0, 1.0]]) + self.dataLim = Bbox.unit() self.viewLim = Bbox.unit() self.transAxes = BboxTransform(Bbox.unit(), self.bbox) @@ -134,14 +186,20 @@ # An affine transformation on the data, generally to limit the # range of the axes self.transProjectionAffine = self.PolarAffine(self.viewLim) - + + # The complete data transformation stack -- from data all the + # way to display coordinates self.transData = self.transScale + self.transProjection + \ (self.transProjectionAffine + self.transAxes) + # This is the transform for theta-axis ticks. It is + # equivalent to transData, except it always puts r == 1.0 at + # the edge of the axis circle. self._xaxis_transform = ( self.transProjection + self.PolarAffine(Bbox.unit()) + self.transAxes) + # The theta labels are moved from radius == 0.0 to radius == 1.1 self._theta_label1_position = Affine2D().translate(0.0, 1.1) self._xaxis_text1_transform = ( self._theta_label1_position + @@ -150,10 +208,14 @@ self._xaxis_text2_transform = ( self._theta_label2_position + self._xaxis_transform) - + + # This is the transform for r-axis ticks. It scales the theta + # axis so the gridlines from 0.0 to 1.0, now go from 0.0 to + # 2pi. self._yaxis_transform = ( Affine2D().scale(npy.pi * 2.0, 1.0) + self.transData) + # The r-axis labels are put at an angle and padded in the r-direction self._r_label1_position = Affine2D().translate(22.5, self._rpad) self._yaxis_text1_transform = ( self._r_label1_position + @@ -166,6 +228,9 @@ Affine2D().scale(1.0 / 360.0, 1.0) + self._yaxis_transform ) + + def get_xaxis_transform(self): + return self._xaxis_transform def get_xaxis_text1_transform(self, pixelPad): return self._xaxis_text1_transform, 'center', 'center' @@ -173,6 +238,9 @@ def get_xaxis_text2_transform(self, pixelPad): return self._xaxis_text2_transform, 'center', 'center' + def get_yaxis_transform(self): + return self._yaxis_transform + def get_yaxis_text1_transform(self, pixelPad): return self._yaxis_text1_transform, 'center', 'center' @@ -181,63 +249,7 @@ def get_axes_patch(self): return Circle((0.5, 0.5), 0.5) - - def start_pan(self, x, y, button): - angle = self._r_label1_position.to_values()[4] / 180.0 * npy.pi - mode = '' - if button == 1: - epsilon = npy.pi / 45.0 - t, r = self.transData.inverted().transform_point((x, y)) - if t >= angle - epsilon and t <= angle + epsilon: - mode = 'drag_r_labels' - elif button == 3: - mode = 'zoom' - self._pan_start = cbook.Bunch( - rmax = self.get_rmax(), - trans = self.transData.frozen(), - trans_inverse = self.transData.inverted().frozen(), - r_label_angle = self._r_label1_position.to_values()[4], - x = x, - y = y, - mode = mode - ) - - def end_pan(self): - del self._pan_start - - def drag_pan(self, button, key, x, y): - p = self._pan_start - - if p.mode == 'drag_r_labels': - startt, startr = p.trans_inverse.transform_point((p.x, p.y)) - t, r = p.trans_inverse.transform_point((x, y)) - - # Deal with theta - dt0 = t - startt - dt1 = startt - t - if abs(dt1) < abs(dt0): - dt = abs(dt1) * sign(dt0) * -1.0 - else: - dt = dt0 * -1.0 - dt = (dt / npy.pi) * 180.0 - - rpad = self._r_label1_position.to_values()[5] - self._r_label1_position.clear().translate( - p.r_label_angle - dt, rpad) - self._r_label2_position.clear().translate( - p.r_label_angle - dt, -rpad) - - elif p.mode == 'zoom': - startt, startr = p.trans_inverse.transform_point((p.x, p.y)) - t, r = p.trans_inverse.transform_point((x, y)) - - dr = r - startr - - # Deal with r - scale = r / startr - self.set_rmax(p.rmax / scale) - def set_rmax(self, rmax): self.viewLim.ymax = rmax angle = self._r_label1_position.to_values()[4] @@ -249,6 +261,11 @@ def get_rmax(self): return self.viewLim.ymax + def set_yscale(self, *args, **kwargs): + Axes.set_yscale(self, *args, **kwargs) + self.yaxis.set_major_locator( + self.RadialLocator(self.yaxis.get_major_locator())) + set_rscale = Axes.set_yscale set_rticks = Axes.set_yticks @@ -330,24 +347,19 @@ set_rgrids.__doc__ = cbook.dedent(set_rgrids.__doc__) % kwdocd - def set_rscale(self, *args, **kwargs): - return self.set_yscale(*args, **kwargs) - def set_xscale(self, *args, **kwargs): raise NotImplementedError("You can not set the xscale on a polar plot.") - def set_yscale(self, *args, **kwargs): - Axes.set_yscale(self, *args, **kwargs) - self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_major_locator())) - def set_xlim(self, *args, **kargs): - # The xlim's a fixed, no matter what you do + # The xlim is fixed, no matter what you do self.viewLim.intervalx = (0.0, npy.pi * 2.0) def format_coord(self, theta, r): 'return a format string formatting the coordinate' theta /= math.pi + # \u03b8: lower-case theta + # \u03c0: lower-case pi + # \u00b0: degree symbol return u'\u03b8=%0.3f\u03c0 (%0.3f\u00b0), r=%0.3f' % (theta, theta * 180.0, r) def get_data_ratio(self): @@ -356,7 +368,65 @@ this should always be 1.0 ''' return 1.0 + + ### Interactive panning + def start_pan(self, x, y, button): + angle = self._r_label1_position.to_values()[4] / 180.0 * npy.pi + mode = '' + if button == 1: + epsilon = npy.pi / 45.0 + t, r = self.transData.inverted().transform_point((x, y)) + if t >= angle - epsilon and t <= angle + epsilon: + mode = 'drag_r_labels' + elif button == 3: + mode = 'zoom' + + self._pan_start = cbook.Bunch( + rmax = self.get_rmax(), + trans = self.transData.frozen(), + trans_inverse = self.transData.inverted().frozen(), + r_label_angle = self._r_label1_position.to_values()[4], + x = x, + y = y, + mode = mode + ) + + def end_pan(self): + del self._pan_start + + def drag_pan(self, button, key, x, y): + p = self._pan_start + + if p.mode == 'drag_r_labels': + startt, startr = p.trans_inverse.transform_point((p.x, p.y)) + t, r = p.trans_inverse.transform_point((x, y)) + + # Deal with theta + dt0 = t - startt + dt1 = startt - t + if abs(dt1) < abs(dt0): + dt = abs(dt1) * sign(dt0) * -1.0 + else: + dt = dt0 * -1.0 + dt = (dt / npy.pi) * 180.0 + + rpad = self._r_label1_position.to_values()[5] + self._r_label1_position.clear().translate( + p.r_label_angle - dt, rpad) + self._r_label2_position.clear().translate( + p.r_label_angle - dt, -rpad) + + elif p.mode == 'zoom': + startt, startr = p.trans_inverse.transform_point((p.x, p.y)) + t, r = p.trans_inverse.transform_point((x, y)) + + dr = r - startr + + # Deal with r + scale = r / startr + self.set_rmax(p.rmax / scale) + # These are a couple of aborted attempts to project a polar plot using # cubic bezier curves. Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-04 18:57:27 UTC (rev 3914) @@ -191,7 +191,7 @@ return Bbox(self.get_points().copy()) frozen.__doc__ = TransformNode.__doc__ - def __array__(self): + def __array__(self, *args, **kwargs): return self.get_points() def _get_xmin(self): Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/src/_backend_agg.cpp 2007-10-04 18:57:27 UTC (rev 3914) @@ -187,7 +187,6 @@ GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), - cliprect(NULL), Ndash(0), dashOffset(0.0), dasha(NULL) { _VERBOSE("GCAgg::GCAgg"); @@ -295,34 +294,14 @@ } } -// MGDTODO: Convert directly from Bbox object (numpy) void GCAgg::_set_clip_rectangle( const Py::Object& gc) { //set the clip rectangle from the gc _VERBOSE("GCAgg::_set_clip_rectangle"); - - delete [] cliprect; - cliprect = NULL; - + Py::Object o ( gc.getAttr( "_cliprect" ) ); - if (o.ptr() == Py_None) { - return; - } - - Py::SeqBase<Py::Object> rect( o ); - - double l = Py::Float(rect[0]) ; - double b = Py::Float(rect[1]) ; - double w = Py::Float(rect[2]) ; - double h = Py::Float(rect[3]) ; - - cliprect = new double[4]; - //todo check for memory alloc failure - cliprect[0] = l; - cliprect[1] = b; - cliprect[2] = w; - cliprect[3] = h; + cliprect = o; } void @@ -387,25 +366,29 @@ }; - template<class R> void -RendererAgg::set_clipbox(double *cliprect, R rasterizer) { +RendererAgg::set_clipbox(Py::Object& cliprect, R rasterizer) { //set the clip rectangle from the gc _VERBOSE("RendererAgg::set_clipbox"); - if (cliprect!=NULL) { + if (cliprect.ptr() != Py_None) { + PyArrayObject* bbox = (PyArrayObject*) PyArray_FromObject(cliprect.ptr(), PyArray_DOUBLE, 2, 2); + + if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) + throw Py::TypeError + ("Expected a Bbox object."); - double l = cliprect[0] ; - double b = cliprect[1] ; - double w = cliprect[2] ; - double h = cliprect[3] ; - - rasterizer->clip_box((int)l, (int)(height-(b+h)), (int)(l+w), (int)(height-b)); + double l = *(double*)PyArray_GETPTR2(bbox, 0, 0); + double b = *(double*)PyArray_GETPTR2(bbox, 0, 1); + double r = *(double*)PyArray_GETPTR2(bbox, 1, 0); + double t = *(double*)PyArray_GETPTR2(bbox, 1, 1); + + rasterizer->clip_box((int)l, (int)(height-t), (int)r, (int)(height-b)); } + _VERBOSE("RendererAgg::set_clipbox done"); - } std::pair<bool, agg::rgba> @@ -485,17 +468,24 @@ RendererAgg::copy_from_bbox(const Py::Tuple& args) { //copy region in bbox to buffer and return swig/agg buffer object args.verify_length(1); + + Py::Object box_obj = args[0]; + + PyArrayObject* bbox = (PyArrayObject*) PyArray_FromObject(box_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) + throw Py::TypeError + ("Expected a Bbox object."); + + double l = *(double*)PyArray_GETPTR2(bbox, 0, 0); + double b = *(double*)PyArray_GETPTR2(bbox, 0, 1); + double r = *(double*)PyArray_GETPTR2(bbox, 1, 0); + double t = *(double*)PyArray_GETPTR2(bbox, 1, 1); - agg::rect r = bbox_to_rect<int>(args[0]); - /* - r.x1 -=5; - r.y1 -=5; - r.x2 +=5; - r.y2 +=5; - */ - int boxwidth = r.x2-r.x1; - int boxheight = r.y2-r.y1; + agg::rect rect((int)l, (int)(height-t), (int)r, (int)(height-b)); + + int boxwidth = rect.x2-rect.x1; + int boxheight = rect.y2-rect.y1; int boxstride = boxwidth*4; agg::buffer buf(boxwidth, boxheight, boxstride, false); if (buf.data ==NULL) { @@ -508,8 +498,8 @@ pixfmt pf(rbuf); renderer_base rb(pf); //rb.clear(agg::rgba(1, 0, 0)); //todo remove me - rb.copy_from(*renderingBuffer, &r, -r.x1, -r.y1); - BufferRegion* reg = new BufferRegion(buf, r, true); + rb.copy_from(*renderingBuffer, &rect, -rect.x1, -rect.y1); + BufferRegion* reg = new BufferRegion(buf, rect, true); return Py::asObject(reg); } @@ -535,64 +525,6 @@ return Py::Object(); } -/** - * Helper function to convert a Python Bbox object to an agg rectangle - */ -template<class T> -agg::rect_base<T> -RendererAgg::bbox_to_rect(const Py::Object& o) { - //return the agg::rect for bbox, flipping y - PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2); - - if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) - throw Py::TypeError - ("Expected a Bbox object."); - - double l = bbox->data[0]; - double b = bbox->data[1]; - double r = bbox->data[2]; - double t = bbox->data[3]; - T height = (T)(b - t); - - agg::rect_base<T> rect((T)l, height-(T)t, (T)r, height-(T)b ) ; - if (!rect.is_valid()) - throw Py::ValueError("Invalid rectangle in bbox_to_rect"); - return rect; -} - -void -RendererAgg::set_clip_from_bbox(const Py::Object& o) { - - // do not puut this in the else below. We want to unconditionally - // clear the clip - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); - - if (o.ptr() != Py_None) { //using clip - // Bbox::check(args[0]) failing; something about cross module? - // set the clip rectangle - // flipy - agg::rect_base<double> r = bbox_to_rect<double>(o); - theRasterizer->clip_box(r.x1, r.y1, r.x2, r.y2); - rendererBase->clip_box((int)r.x1, (int)r.y1, (int)r.x2, (int)r.y2); - } - -} - -/****************************/ - -int RendererAgg::intersectCheck(double yCoord, double x1, double y1, double x2, double y2, int* intersectPoint) -{ - /* Returns 0 if no intersection or 1 if yes */ - /* If yes, changes intersectPoint to the x coordinate of the point of intersection */ - if ((y1>=yCoord) != (y2>=yCoord)) { - /* Don't need to check for y1==y2 because the above condition rejects it automatically */ - *intersectPoint = (int)( ( x1 * (y2 - yCoord) + x2 * (yCoord - y1) ) / (y2 - y1) + 0.5); - return 1; - } - return 0; -} - bool RendererAgg::render_clippath(const GCAgg& gc) { typedef agg::conv_transform<PathIterator> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; @@ -633,6 +565,7 @@ typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; typedef agg::renderer_scanline_bin_solid<amask_ren_type> amask_bin_renderer_type; + rendererBase->reset_clipping(true); theRasterizer->reset_clipping(); args.verify_length(5, 6); @@ -802,6 +735,7 @@ }; +// MGDTODO: Support clip paths Py::Object RendererAgg::draw_text_image(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_text"); @@ -877,6 +811,7 @@ } +// MGDTODO: Support clip paths Py::Object RendererAgg::draw_image(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); @@ -885,8 +820,11 @@ float x = Py::Float(args[0]); float y = Py::Float(args[1]); Image *image = static_cast<Image*>(args[2].ptr()); + Py::Object box_obj = args[3]; - set_clip_from_bbox(args[3]); + theRasterizer->reset_clipping(); + rendererBase->reset_clipping(true); + set_clipbox(box_obj, rendererBase); pixfmt pixf(*(image->rbufOut)); Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-10-04 17:22:01 UTC (rev 3913) +++ branches/transforms/src/_backend_agg.h 2007-10-04 18:57:27 UTC (rev 3914) @@ -111,7 +111,6 @@ ~GCAgg() { delete [] dasha; - delete [] cliprect; } double dpi; @@ -126,7 +125,7 @@ double alpha; agg::rgba color; - double *cliprect; + Py::Object cliprect; Py::Object clippath; agg::trans_affine clippath_trans; @@ -214,16 +213,12 @@ const int debug; protected: - template<class T> - agg::rect_base<T> bbox_to_rect( const Py::Object& o); double points_to_pixels( const Py::Object& points); double points_to_pixels_snapto( const Py::Object& points); - int intersectCheck(double, double, double, double, double, int*); - void set_clip_from_bbox(const Py::Object& o); agg::rgba rgb_to_color(const Py::SeqBase<Py::Object>& rgb, double alpha); facepair_t _get_rgba_face(const Py::Object& rgbFace, double alpha); template<class R> - void set_clipbox(double *cliprect, R rasterizer); + void set_clipbox(Py::Object& cliprect, R rasterizer); bool render_clippath(const GCAgg& gc); private: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-04 17:22:05
|
Revision: 3913 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3913&view=rev Author: mdboom Date: 2007-10-04 10:22:01 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Sharing axes across figures is much easier now (it is the same as within the same figure), so this demo has been updated. Modified Paths: -------------- branches/transforms/examples/shared_axis_across_figures.py Modified: branches/transforms/examples/shared_axis_across_figures.py =================================================================== --- branches/transforms/examples/shared_axis_across_figures.py 2007-10-04 17:21:26 UTC (rev 3912) +++ branches/transforms/examples/shared_axis_across_figures.py 2007-10-04 17:22:01 UTC (rev 3913) @@ -10,17 +10,18 @@ fig2 = figure() ax1 = fig1.add_subplot(111) -ax2 = fig2.add_subplot(111) +ax2 = fig2.add_subplot(111, sharex=ax1, sharey=ax1) ax1.plot(numpy.random.rand(100), 'o') ax2.plot(numpy.random.rand(100), 'o') -ax1.sharex_foreign(ax2) -ax2.sharex_foreign(ax1) +# In the latest release, it is no longer necessary to do anything +# special to share axes across figures: -ax1.sharey_foreign(ax2) -ax2.sharey_foreign(ax1) +# ax1.sharex_foreign(ax2) +# ax2.sharex_foreign(ax1) +# ax1.sharey_foreign(ax2) +# ax2.sharey_foreign(ax1) - show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-10-04 17:21:31
|
Revision: 3912 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3912&view=rev Author: mdboom Date: 2007-10-04 10:21:26 -0700 (Thu, 04 Oct 2007) Log Message: ----------- Lots of new docstrings. Reasonably good state for polar plots. r-axis labels can be dragged on polar plots. r-scale can be zoomed on polar plot. Lots of other minor changes too numerous to mention. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/projections/polar.py branches/transforms/lib/matplotlib/pyplot.py branches/transforms/lib/matplotlib/ticker.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -478,13 +478,12 @@ """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} martist.Artist.__init__(self) self._position = mtransforms.Bbox.from_lbwh(*rect) - self._originalPosition = copy.deepcopy(self._position) + self._originalPosition = self._position.frozen() self.set_axes(self) self.set_aspect('auto') self.set_adjustable('box') self.set_anchor('C') - # MGDTODO: Check that the axes being shared are scalable self._sharex = sharex self._sharey = sharey if sharex is not None: @@ -540,69 +539,6 @@ self.yaxis = maxis.YAxis(self) self._update_transScale() - def sharex_foreign(self, axforeign): - """ - You can share your x-axis view limits with another Axes in the - same Figure by using the sharex and sharey property of the - Axes. But this doesn't work for Axes in a different figure. - This function sets of the callbacks so that when the xaxis of - this Axes or the Axes in a foreign figure are changed, both - will be synchronized. - - The connection ids for the self.callbacks and - axforeign.callbacks cbook.CallbackRegistry instances are - returned in case you want to disconnect the coupling - """ - - def follow_foreign_xlim(ax): - xmin, xmax = axforeign.get_xlim() - # do not emit here or we'll get a ping png effect - self.set_xlim(xmin, xmax, emit=False) - self.figure.canvas.draw_idle() - - def follow_self_xlim(ax): - xmin, xmax = self.get_xlim() - # do not emit here or we'll get a ping png effect - axforeign.set_xlim(xmin, xmax, emit=False) - axforeign.figure.canvas.draw_idle() - - - cidForeign = axforeign.callbacks.connect('xlim_changed', follow_foreign_xlim) - cidSelf = self.callbacks.connect('xlim_changed', follow_self_xlim) - return cidSelf, cidForeign - - - def sharey_foreign(self, axforeign): - """ - You can share your y-axis view limits with another Axes in the - same Figure by using the sharey and sharey property of the - Axes. But this doesn't work for Axes in a different figure. - This function sets of the callbacks so that when the yaxis of - this Axes or the Axes in a foreign figure are changed, both - will be synchronized. - - The connection ids for the self.callbacks and - axforeign.callbacks cbook.CallbackRegistry instances are - returned in case you want to disconnect the coupling - """ - - def follow_foreign_ylim(ax): - ymin, ymax = axforeign.get_ylim() - # do not emit here or we'll get a ping pong effect - self.set_ylim(ymin, ymax, emit=False) - self.figure.canvas.draw_idle() - - def follow_self_ylim(ax): - ymin, ymax = self.get_ylim() - # do not emit here or we'll get a ping pong effect - axforeign.set_ylim(ymin, ymax, emit=False) - axforeign.figure.canvas.draw_idle() - - - cidForeign = axforeign.callbacks.connect('ylim_changed', follow_foreign_ylim) - cidSelf = self.callbacks.connect('ylim_changed', follow_self_ylim) - return cidSelf, cidForeign - def set_figure(self, fig): """ Set the Axes figure @@ -629,10 +565,6 @@ # It is assumed that this part will have non-linear components self.transScale = mtransforms.TransformWrapper(mtransforms.IdentityTransform()) - self._set_transData() - - # MGDTODO: Rename this method - def _set_transData(self): # A (possibly non-linear) projection on the (already scaled) data self.transProjection = mtransforms.IdentityTransform() @@ -648,34 +580,38 @@ self._yaxis_transform = mtransforms.blended_transform_factory( self.axes.transAxes, self.axes.transData) + self.transData.write_graphviz(open("trans.dot", "w")) + def get_xaxis_transform(self): return self._xaxis_transform def get_xaxis_text1_transform(self, pad_pixels): return (self._xaxis_transform + - mtransforms.Affine2D().translate(0, -1 * pad_pixels)) + mtransforms.Affine2D().translate(0, -1 * pad_pixels), + "top", "center") def get_xaxis_text2_transform(self, pad_pixels): return (self._xaxis_transform + - mtransforms.Affine2D().translate(0, pad_pixels)) + mtransforms.Affine2D().translate(0, pad_pixels), + "top", "center") def get_yaxis_transform(self): return self._yaxis_transform def get_yaxis_text1_transform(self, pad_pixels): return (self._yaxis_transform + - mtransforms.Affine2D().translate(-1 * pad_pixels, 0)) + mtransforms.Affine2D().translate(-1 * pad_pixels, 0), + "center", "right") def get_yaxis_text2_transform(self, pad_pixels): return (self._yaxis_transform + - mtransforms.Affine2D().translate(pad_pixels, 0)) + mtransforms.Affine2D().translate(pad_pixels, 0), + "center", "right") def _update_transScale(self): self.transScale.set( mtransforms.blended_transform_factory( self.xaxis.get_transform(), self.yaxis.get_transform())) - - self.transData.make_graphviz(open("trans.dot", "w")) def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' @@ -1164,7 +1100,8 @@ # limits and set the bound to be the bounds of the xydata. # Otherwise, it will compute the bounds of it's current data # and the data in xydata - xys = npy.asarray(xys) + if not ma.isMaskedArray(xys): + xys = npy.asarray(xys) self.update_datalim_numerix(xys[:, 0], xys[:, 1]) def update_datalim_numerix(self, x, y): @@ -1535,7 +1472,9 @@ for other in self._shared_x_axes.get_siblings(self): if other is not self: other.set_xlim(self.viewLim.intervalx, emit=False) - + + self.figure.canvas.draw_idle() + return xmin, xmax def get_xscale(self): @@ -1641,7 +1580,9 @@ for other in self._shared_y_axes.get_siblings(self): if other is not self: other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False) - + + self.figure.canvas.draw_idle() + return ymin, ymax def get_yscale(self): @@ -1795,8 +1736,19 @@ """ self._navigate_mode = b - def drag_pan(self, button, key, startx, starty, dx, dy, - start_lim, start_trans): + def start_pan(self, x, y, button): + self._pan_start = cbook.Bunch( + lim = self.viewLim.frozen(), + trans = self.transData.frozen(), + trans_inverse = self.transData.inverted().frozen(), + x = x, + y = y + ) + + def end_pan(self): + del self._pan_start + + def drag_pan(self, button, key, x, y): def format_deltas(key, dx, dy): if key=='control': if(abs(dx)>abs(dy)): @@ -1818,22 +1770,24 @@ dx=dx/abs(dx)*abs(dy) return (dx,dy) + p = self._pan_start + dx = x - p.x + dy = y - p.y if button == 1: - inverse = start_trans.inverted() dx, dy = format_deltas(key, dx, dy) - result = self.bbox.frozen().translated(-dx, -dy).transformed(inverse) + result = self.bbox.frozen().translated(-dx, -dy) \ + .transformed(p.trans_inverse) elif button == 3: try: # MGDTODO: This is broken with log scales - inverse = start_trans.inverted() dx, dy = format_deltas(key, dx, dy) dx = -dx / float(self.bbox.width) dy = -dy / float(self.bbox.height) - xmin, ymin, xmax, ymax = start_lim.lbrt + xmin, ymin, xmax, ymax = p.lim.lbrt alpha = npy.power(10.0, (dx, dy)) - start = inverse.transform_point((startx, starty)) - lim_points = start_lim.get_points() + start = p.trans_inverse.transform_point((p.x, p.y)) + lim_points = p.lim.get_points() result = start + alpha * (lim_points - start) result = mtransforms.Bbox(result) except OverflowError: @@ -5214,485 +5168,16 @@ label.set_visible(firstcol) def subplot_class_factory(axes_class=None): - # MGDTODO: This is a little bit strange to make a new class on the - # fly like this, but it keeps things relatively similar to how they - # were before + # This makes a new class that inherits from SubclassBase and the + # given axes_class (which is assumed to be a subclass of Axes). + + # This is perhaps a little bit roundabout to make a new class on + # the fly like this, but it means that a new Subplot class does + # not have to be created for every type of Axes. new_class = new.classobj("%sSubplot" % (axes_class.__name__), (SubplotBase, axes_class), {'_axes_class': axes_class}) return new_class - - -class PolarAxes(Axes): - """ - Make a PolarAxes. The rectangular bounding box of the axes is given by - - - PolarAxes(position=[left, bottom, width, height]) - - where all the arguments are fractions in [0,1] which specify the - fraction of the total figure window. - - axisbg is the color of the axis background - - Attributes: - thetagridlines : a list of Line2D for the theta grids - rgridlines : a list of Line2D for the radial grids - thetagridlabels : a list of Text for the theta grid labels - rgridlabels : a list of Text for the theta grid labels - - """ - - RESOLUTION = 100 - - def __init__(self, *args, **kwarg): - """ - See Axes base class for args and kwargs documentation - """ - Axes.__init__(self, *args, **kwarg) - self.set_aspect('equal', adjustable='box', anchor='C') - self.cla() - def _init_axis(self): - "nuthin to do" - self.xaxis = None - self.yaxis = None - - - def _set_lim_and_transforms(self): - """ - set the dataLim and viewLim BBox attributes and the - transData and transAxes Transformation attributes - """ - - # the lim are theta, r - - # MGDTODO -# Bbox = mtrans.Bbox -# Value = mtrans.Value -# Point = mtrans.Point -# self.dataLim = Bbox( Point( Value(5/4.*math.pi), Value(math.sqrt(2))), -# Point( Value(1/4.*math.pi), Value(math.sqrt(2)))) -# self.viewLim = Bbox( Point( Value(5/4.*math.pi), Value(math.sqrt(2))), -# Point( Value(1/4.*math.pi), Value(math.sqrt(2)))) - -# self.transData = mtrans.NonseparableTransformation( -# self.viewLim, self.bbox, -# mtrans.FuncXY(mtrans.POLAR)) -# self.transAxes = mtrans.get_bbox_transform( -# mtrans.unit_bbox(), self.bbox) - pass - - def contains(self,mouseevent): - """Test whether the mouse event occured in the axes. - - Returns T/F, {} - """ - if callable(self._contains): return self._contains(self,mouseevent) - - x,y = self.axes.transAxes.inverse_xy_tup((mouseevent.x,mouseevent.y)) - #print "Polar: x,y = ",x,y - inside = (x-0.5)**2 + (y-0.5)**2 <= 0.25 - return inside,{} - - def cla(self): - 'Clear the current axes' - - # init these w/ some arbitrary numbers - they'll be updated as - # data is added to the axes - - self._get_lines = _process_plot_var_args(self) - self._get_patches_for_fill = _process_plot_var_args(self, 'fill') - - self._gridOn = rcParams['polaraxes.grid'] - self.thetagridlabels = [] - self.thetagridlines = [] - self.rgridlabels = [] - self.rgridlines = [] - - self.lines = [] - self.images = [] - self.patches = [] - self.artists = [] - self.collections = [] - self.texts = [] # text in axis coords - self.legend_ = None - - self.grid(self._gridOn) - props = font_manager.FontProperties(size=rcParams['axes.titlesize']) - self.title = mtext.Text( - x=0.5, y=1.05, text='', - fontproperties=props, - verticalalignment='bottom', - horizontalalignment='center', - ) - self.title.set_transform(self.transAxes) - - self._set_artist_props(self.title) - - - self.thetas = npy.linspace(0, 2*math.pi, self.RESOLUTION) - - verts = zip(self.thetas, npy.ones(self.RESOLUTION)) - self.axesPatch = mpatches.Polygon( - verts, - facecolor=self._axisbg, - edgecolor=rcParams['axes.edgecolor'], - ) - - - - self.axesPatch.set_figure(self.figure) - self.axesPatch.set_transform(self.transData) - self.axesPatch.set_linewidth(rcParams['axes.linewidth']) - self.axison = True - - # we need to set a view and data interval from 0->rmax to make - # the formatter and locator work correctly - # MGDTODO - Value = mtrans.Value - Interval = mtrans.Interval - self.rintv = Interval(Value(0), Value(1)) - self.rintd = Interval(Value(0), Value(1)) - - self.rformatter = mticker.ScalarFormatter() - self.rformatter.set_view_interval(self.rintv) - self.rformatter.set_data_interval(self.rintd) - - class RadialLocator(mticker.AutoLocator): - 'enforce strictly positive radial ticks' - - def __call__(self): - ticks = mticker.AutoLocator.__call__(self) - return [t for t in ticks if t>0] - - self.rlocator = RadialLocator() - self.rlocator.set_view_interval(self.rintv) - self.rlocator.set_data_interval(self.rintd) - - - angles = npy.arange(0, 360, 45) - radii = npy.arange(0.2, 1.1, 0.2) - self.set_thetagrids(angles) - self.set_rgrids(radii) - - def get_children(self): - 'return a list of child artists' - children = [] - children.extend(self.rgridlines) - children.extend(self.rgridlabels) - children.extend(self.thetagridlines) - children.extend(self.thetagridlabels) - children.extend(self.lines) - children.extend(self.patches) - children.extend(self.texts) - children.extend(self.artists) - children.extend(self.images) - if self.legend_ is not None: - children.append(self.legend_) - children.extend(self.collections) - children.append(self.title) - children.append(self.axesPatch) - return children - - - def set_rmax(self, rmax): - self.rintv.set_bounds(0, rmax) - self.regrid(rmax) - - def grid(self, b): - 'Set the axes grids on or off; b is a boolean' - self._gridOn = b - - def regrid(self, rmax): - rmax = float(rmax) - self.axesPatch.xy = zip(self.thetas, rmax*npy.ones(self.RESOLUTION)) - - val = rmax*math.sqrt(2) - self.viewLim.intervaly().set_bounds(val, val) - - ticks = self.rlocator() - self.set_rgrids(ticks) - self.rformatter.set_locs(ticks) - - for t in self.thetagridlabels: - t.set_y(1.05*rmax) - - r = npy.linspace(0, rmax, self.RESOLUTION) - for l in self.thetagridlines: - l.set_ydata(r) - - def autoscale_view(self, scalex=True, scaley=True): - 'set the view limits to include all the data in the axes' - self.rintd.set_bounds(0, self.get_rmax()) - rmin, rmax = self.rlocator.autoscale() - self.rintv.set_bounds(rmin, rmax) - self.regrid(rmax) - - def set_rgrids(self, radii, labels=None, angle=22.5, rpad=0.05, **kwargs): - """ - set the radial locations and labels of the r grids - - The labels will appear at radial distances radii at angle - - labels, if not None, is a len(radii) list of strings of the - labels to use at each angle. - - if labels is None, the self.rformatter will be used - - rpad is a fraction of the max of radii which will pad each of - the radial labels in the radial direction. - - Return value is a list of lines, labels where the lines are - lines.Line2D instances and the labels are text.Text - instances - - kwargs control the rgrid Text label properties: - %(Text)s - - ACCEPTS: sequence of floats - """ - - - radii = npy.asarray(radii) - rmin = radii.min() - if rmin<=0: - raise ValueError('radial grids must be strictly positive') - - rpad = rpad * max(radii) - cbook.popall(self.rgridlines) - - theta = npy.linspace(0., 2*math.pi, self.RESOLUTION) - ls = rcParams['grid.linestyle'] - color = rcParams['grid.color'] - lw = rcParams['grid.linewidth'] - - rmax = self.get_rmax() - for r in radii: - r = npy.ones(self.RESOLUTION)*r - line = mlines.Line2D(theta, r, linestyle=ls, color=color, linewidth=lw, - figure=self.figure) - line.set_transform(self.transData) - self.rgridlines.append(line) - - cbook.popall(self.rgridlabels) - - - color = rcParams['xtick.color'] - - - props = font_manager.FontProperties(size=rcParams['xtick.labelsize']) - if labels is None: - labels = [self.rformatter(r,0) for r in radii] - for r,l in zip(radii, labels): - t = mtext.Text(angle/180.*math.pi, r+rpad, l, - fontproperties=props, color=color, - horizontalalignment='center', verticalalignment='center') - t.set_transform(self.transData) - t.update(kwargs) - self._set_artist_props(t) - t.set_clip_on(False) - self.rgridlabels.append(t) - - return self.rgridlines, self.rgridlabels - set_rgrids.__doc__ = cbook.dedent(set_rgrids.__doc__) % martist.kwdocd - - def set_thetagrids(self, angles, labels=None, fmt='%d', frac = 1.1, - **kwargs): - """ - set the angles at which to place the theta grids (these - gridlines are equal along the theta dimension). angles is in - degrees - - labels, if not None, is a len(angles) list of strings of the - labels to use at each angle. - - if labels is None, the labels with be fmt%%angle - - frac is the fraction of the polar axes radius at which to - place the label (1 is the edge).Eg 1.05 isd outside the axes - and 0.95 is inside the axes - - Return value is a list of lines, labels where the lines are - lines.Line2D instances and the labels are Text - instances: - - kwargs are optional text properties for the labels - %(Text)s - ACCEPTS: sequence of floats - """ - cbook.popall(self.thetagridlines) - ox, oy = 0,0 - ls = rcParams['grid.linestyle'] - color = rcParams['grid.color'] - lw = rcParams['grid.linewidth'] - - rmax = self.get_rmax() - r = npy.linspace(0., rmax, self.RESOLUTION) - for a in angles: - theta = npy.ones(self.RESOLUTION)*a/180.*math.pi - line = mlines.Line2D( - theta, r, linestyle=ls, color=color, linewidth=lw, - figure=self.figure) - line.set_transform(self.transData) - self.thetagridlines.append(line) - - cbook.popall(self.thetagridlabels) - - color = rcParams['xtick.color'] - - props = font_manager.FontProperties(size=rcParams['xtick.labelsize']) - r = frac*rmax - if labels is None: - labels = [fmt%a for a in angles] - for a,l in zip(angles, labels): - t = mtext.Text(a/180.*math.pi, r, l, fontproperties=props, color=color, - horizontalalignment='center', verticalalignment='center') - t.set_transform(self.transData) - t.update(kwargs) - self._set_artist_props(t) - t.set_clip_on(False) - self.thetagridlabels.append(t) - return self.thetagridlines, self.thetagridlabels - set_thetagrids.__doc__ = cbook.dedent(set_thetagrids.__doc__) % martist.kwdocd - - def get_rmax(self): - 'get the maximum radius in the view limits dimension' - vmin, vmax = self.dataLim.intervaly().get_bounds() - return max(vmin, vmax) - - def draw(self, renderer): - if not self.get_visible(): return - renderer.open_group('polar_axes') - self.apply_aspect(1) - self.transData.freeze() # eval the lazy objects - self.transAxes.freeze() # eval the lazy objects - - verts = self.axesPatch.get_verts() - tverts = self.transData.seq_xy_tups(verts) - - #for i,v,t in zip(range(len(verts)), verts, tverts): - # print i,v,t - - - - l,b,w,h = self.figure.bbox.get_bounds() - clippath = agg.path_storage() - for i, xy in enumerate(tverts): - x,y = xy - y = h-y - if i==0: clippath.move_to(x, y) - else: clippath.line_to(x, y) - clippath.close_polygon() - - #self._update_axes() - if self.axison: - if self._frameon: self.axesPatch.draw(renderer) - - if self._gridOn: - for l in self.rgridlines: - l.set_clip_path(clippath) - l.draw(renderer) - - for l in self.thetagridlines: - l.set_clip_path(clippath) - l.draw(renderer) - - for a in self.lines:# + self.patches: - a.set_clip_path(clippath) - - artists = [] - artists.extend(self.lines) - artists.extend(self.texts) - artists.extend(self.collections) - artists.extend(self.patches) - artists.extend(self.artists) - - dsu = [ (a.zorder, a) for a in artists] - dsu.sort() - - for zorder, a in dsu: - a.draw(renderer) - - - for t in self.thetagridlabels+self.rgridlabels: - t.draw(renderer) - - if self.legend_ is not None: - self.legend_.draw(renderer) - - self.title.draw(renderer) - - - - self.transData.thaw() # release the lazy objects - self.transAxes.thaw() # release the lazy objects - renderer.close_group('polar_axes') - - - def format_coord(self, theta, r): - 'return a format string formatting the coordinate' - theta /= math.pi - return 'theta=%1.2fpi, r=%1.3f'%(theta, r) - - - def has_data(self): - 'return true if any artists have been added to axes' - return len(self.lines)+len(self.collections) - - def set_xlabel(self, xlabel, fontdict=None, **kwargs): - 'xlabel not implemented' - raise NotImplementedError('xlabel not defined for polar axes (yet)') - - def set_ylabel(self, ylabel, fontdict=None, **kwargs): - 'ylabel not implemented' - raise NotImplementedError('ylabel not defined for polar axes (yet)') - - def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs): - 'xlim not implemented' - raise NotImplementedError('xlim not meaningful for polar axes') - - def set_ylim(self, ymin=None, ymax=None, emit=True, **kwargs): - 'ylim not implemented' - raise NotImplementedError('ylim not meaningful for polar axes') - - def get_xscale(self): - 'return the xaxis scale string' - return 'polar' - - def get_yscale(self): - 'return the yaxis scale string' - return 'polar' - - def table(self, *args, **kwargs): - """ - TABLE(*args, **kwargs) - Not implemented for polar axes - """ - raise NotImplementedError('table not implemented for polar axes') - - - -class PolarSubplot(SubplotBase, PolarAxes): - """ - Create a polar subplot with - - PolarSubplot(numRows, numCols, plotNum) - - where plotNum=1 is the first plot number and increasing plotNums - fill rows first. max(plotNum)==numRows*numCols - - You can leave out the commas if numRows<=numCols<=plotNum<10, as - in - - Subplot(211) # 2 rows, 1 column, first (upper) plot - """ - def __str__(self): - return "PolarSubplot(%gx%g)"%(self.figW,self.figH) - def __init__(self, fig, *args, **kwargs): - SubplotBase.__init__(self, fig, *args) - PolarAxes.__init__( - self, fig, - [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) martist.kwdocd['Axes'] = martist.kwdocd['Subplot'] = martist.kwdoc(Axes) Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -166,7 +166,7 @@ def draw(self, renderer): if not self.get_visible(): return renderer.open_group(self.__name__) - midPoint = interval_contains_open(self.get_view_interval(), self.get_loc() ) + midPoint = interval_contains(self.get_view_interval(), self.get_loc()) if midPoint: if self.gridOn: @@ -176,8 +176,10 @@ if self.tick2On: self.tick2line.draw(renderer) - if self.label1On: self.label1.draw(renderer) - if self.label2On: self.label2.draw(renderer) + if self.label1On: + self.label1.draw(renderer) + if self.label2On: + self.label2.draw(renderer) renderer.close_group(self.__name__) @@ -237,17 +239,19 @@ # get the affine as an a,b,c,d,tx,ty list # x in data coords, y in axes coords #t = Text( + trans, vert, horiz = self.axes.get_xaxis_text1_transform(self._padPixels) + t = TextWithDash( x=loc, y=0, fontproperties=FontProperties(size=rcParams['xtick.labelsize']), color=rcParams['xtick.color'], - verticalalignment='top', - horizontalalignment='center', + verticalalignment=vert, + horizontalalignment=horiz, dashdirection=0, xaxis=True, ) - t.set_transform(self.axes.get_xaxis_text1_transform(self._padPixels)) + t.set_transform(trans) self._set_artist_props(t) return t @@ -257,17 +261,19 @@ 'Get the default Text 2 instance' # x in data coords, y in axes coords #t = Text( + trans, vert, horiz = self.axes.get_xaxis_text2_transform(self._padPixels) + t = TextWithDash( x=loc, y=1, fontproperties=FontProperties(size=rcParams['xtick.labelsize']), color=rcParams['xtick.color'], - verticalalignment='bottom', + verticalalignment=vert, dashdirection=1, xaxis=True, - horizontalalignment='center', + horizontalalignment=horiz, ) - t.set_transform(self.axes.get_xaxis_text2_transform(self._padPixels)) + t.set_transform(trans) self._set_artist_props(t) return t @@ -277,7 +283,6 @@ l = Line2D( xdata=(loc,), ydata=(0,), color='k', linestyle = 'None', - antialiased=False, marker = self._xtickmarkers[0], markersize=self._size, ) @@ -291,7 +296,6 @@ l = Line2D( xdata=(loc,), ydata=(1,), color='k', linestyle = 'None', - antialiased=False, marker = self._xtickmarkers[1], markersize=self._size, ) @@ -307,7 +311,6 @@ color=rcParams['grid.color'], linestyle=rcParams['grid.linestyle'], linewidth=rcParams['grid.linewidth'], - antialiased=False, ) l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) @@ -356,16 +359,18 @@ 'Get the default Text instance' # x in axes coords, y in data coords #t = Text( - t = TextWithDash( + trans, vert, horiz = self.axes.get_yaxis_text1_transform(self._padPixels) + + t = TextWithDash( x=0, y=loc, fontproperties=FontProperties(size=rcParams['ytick.labelsize']), color=rcParams['ytick.color'], - verticalalignment='center', - horizontalalignment='right', + verticalalignment=vert, + horizontalalignment=horiz, dashdirection=0, xaxis=False, ) - t.set_transform(self.axes.get_yaxis_text1_transform(self._padPixels)) + t.set_transform(trans) #t.set_transform( self.axes.transData ) self._set_artist_props(t) return t @@ -374,16 +379,18 @@ 'Get the default Text instance' # x in axes coords, y in data coords #t = Text( + trans, vert, horiz = self.axes.get_yaxis_text2_transform(self._padPixels) + t = TextWithDash( x=1, y=loc, fontproperties=FontProperties(size=rcParams['ytick.labelsize']), color=rcParams['ytick.color'], - verticalalignment='center', + verticalalignment=vert, dashdirection=1, xaxis=False, - horizontalalignment='left', + horizontalalignment=horiz, ) - t.set_transform(self.axes.get_yaxis_text2_transform(self._padPixels)) + t.set_transform(trans) self._set_artist_props(t) return t @@ -392,7 +399,6 @@ # x in axes coords, y in data coords l = Line2D( (0,), (loc,), color='k', - antialiased=False, marker = self._ytickmarkers[0], linestyle = 'None', markersize=self._size, @@ -405,7 +411,6 @@ 'Get the default line2D instance' # x in axes coords, y in data coords l = Line2D( (1,), (0,), color='k', - antialiased=False, marker = self._ytickmarkers[1], linestyle = 'None', markersize=self._size, @@ -422,7 +427,6 @@ color=rcParams['grid.color'], linestyle=rcParams['grid.linestyle'], linewidth=rcParams['grid.linewidth'], - antialiased=False, ) l.set_transform(self.axes.get_yaxis_transform()) @@ -523,8 +527,8 @@ def get_children(self): children = [self.label] - majorticks = self.get_major_ticks(len(self.major.locator())) - minorticks = self.get_minor_ticks(len(self.minor.locator())) + majorticks = self.get_major_ticks() + minorticks = self.get_minor_ticks() children.extend(majorticks) children.extend(minorticks) @@ -560,8 +564,8 @@ def set_clip_path(self, clippath, transform=None): Artist.set_clip_path(self, clippath, transform) - majorticks = self.get_major_ticks(len(self.major.locator())) - minorticks = self.get_minor_ticks(len(self.minor.locator())) + majorticks = self.get_major_ticks() + minorticks = self.get_minor_ticks() for child in self.majorTicks + self.minorTicks: child.set_clip_path(clippath, transform) @@ -729,9 +733,11 @@ 'Get the formatter of the minor ticker' return self.minor.formatter - def get_major_ticks(self, numticks): + def get_major_ticks(self, numticks=None): 'get the tick instances; grow as necessary' - + if numticks is None: + numticks = len(self.get_major_locator()()) + if len(self.majorTicks)<numticks: # update the new tick label properties from the old protoTick = self.majorTicks[0] @@ -746,8 +752,11 @@ return ticks - def get_minor_ticks(self, numticks): + def get_minor_ticks(self, numticks=None): 'get the minor tick instances; grow as necessary' + if numticks is None: + numticks = len(self.get_minor_locator()()) + if len(self.minorTicks)<numticks: protoTick = self.minorTicks[0] for i in range(numticks-len(self.minorTicks)): Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -34,40 +34,22 @@ def draw_path(self, gc, path, transform, rgbFace=None): """ - Handles the caching of the native path associated with the - given path and calls the underlying backend's _draw_path to - actually do the drawing. + Draws a Path instance using the given affine transform. """ - # MGDTODO: Update docstring raise NotImplementedError def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): """ - This method is currently underscore hidden because the - draw_markers method is being used as a sentinel for newstyle - backend drawing + Draws a marker at each of the vertices in path. This includes + all vertices, including control points on curves. To avoid + that behavior, those vertices should be removed before calling + this function. - path - a matplotlib.agg.path_storage instance - - Draw the marker specified in path with graphics context gc at - each of the locations in arrays x and y. trans is a - matplotlib.transforms.Transformation instance used to - transform x and y to display coords. It consists of an - optional nonlinear component and an affine. You can access - these two components as - - if transform.need_nonlinear(): - x,y = transform.nonlinear_only_numerix(x, y) - # the a,b,c,d,tx,ty affine which transforms x and y - vec6 = transform.as_vec6_val() - ...backend dependent affine... + marker_trans is an affine transform applied to the marker. + trans is an affine transform applied to the path. """ - # MGDTODO: Update docstring raise NotImplementedError - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): - raise NotImplementedError - def get_image_magnification(self): """ Get the factor by which to magnify images passed to draw_image. @@ -1560,7 +1542,8 @@ self._xypress=[] for i, a in enumerate(self.canvas.figure.get_axes()): if x is not None and y is not None and a.in_axes(event) and a.get_navigate(): - self._xypress.append((x, y, a, i, a.viewLim.frozen(), a.transData.frozen())) + a.start_pan(x, y, event.button) + self._xypress.append((a, i)) self.canvas.mpl_disconnect(self._idDrag) self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan) @@ -1597,14 +1580,12 @@ lims.append( (xmin, xmax, ymin, ymax) ) # Store both the original and modified positions pos.append( ( - copy.copy(a.get_position(True)), - copy.copy(a.get_position() )) ) + a.get_position(True).frozen(), + a.get_position().frozen() ) ) self._views.push(lims) self._positions.push(pos) self.set_history_buttons() - - def release(self, event): 'this will be called whenever mouse button is released' pass @@ -1613,6 +1594,8 @@ 'the release mouse button callback in pan/zoom mode' self.canvas.mpl_disconnect(self._idDrag) self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.mouse_move) + for a, ind in self._xypress: + a.end_pan() if not self._xypress: return self._xypress = None self._button_pressed=None @@ -1623,12 +1606,10 @@ def drag_pan(self, event): 'the drag callback in pan/zoom mode' - for lastx, lasty, a, ind, old_lim, old_trans in self._xypress: + for a, ind in self._xypress: #safer to use the recorded button at the press than current button: #multiple button can get pressed during motion... - dx, dy = event.x - lastx, event.y - lasty - a.drag_pan(self._button_pressed, event.key, lastx, lasty, dx, dy, - old_lim, old_trans) + a.drag_pan(self._button_pressed, event.key, event.x, event.y) self.dynamic_update() def release_zoom(self, event): Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -96,15 +96,6 @@ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles """ - # MGDTODO: Renderers seem to get created and destroyed fairly - # often so the paths are cached at the class (not instance) level. - # However, this dictionary is only directly used by RendererBase, - # so it seems funny to define it here. However, if we didn't, the - # native paths would be shared across renderers, which is - # obviously bad. Seems like a good use of metaclasses, but that - # also seems like a heavy solution for a minor problem. - _native_paths = weakref.WeakKeyDictionary() - debug=1 texd = {} # a cache of tex image rasters def __init__(self, width, height, dpi): @@ -130,12 +121,10 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') - # MGDTODO: Just adding helpful asserts. This can be removed in the future def draw_path(self, gc, path, trans, rgbFace=None): assert trans.is_affine self._renderer.draw_path(gc, path, trans.frozen(), rgbFace) - # MGDTODO: Just adding helpful asserts. This can be removed in the future def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): assert marker_trans.is_affine assert trans.is_affine Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/legend.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -256,10 +256,10 @@ 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] - bboxesAll = bboxesText bboxesAll.extend(bboxesHandles) bbox = Bbox.union(bboxesAll) + self.save = bbox ibox = bbox.inverse_transformed(self.get_transform()) @@ -558,7 +558,7 @@ handle.set_height(h/2) # Set the data for the legend patch - bbox = copy.copy(self._get_handle_text_bbox(renderer)) + bbox = self._get_handle_text_bbox(renderer).frozen() bbox = bbox.expanded(1 + self.pad, 1 + self.pad) l, b, w, h = bbox.bounds Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -393,7 +393,6 @@ def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units - # MGDTODO: Deal with units x = ma.asarray(self.convert_xunits(self._xorig), float) y = ma.asarray(self.convert_yunits(self._yorig), float) @@ -414,12 +413,12 @@ self._x = self._xy[:, 0] # just a view self._y = self._xy[:, 1] # just a view self._logcache = None + # Masked arrays are now handled by the Path class itself self._path = Path(self._xy, closed=False) self._transformed_path = TransformedPath(self._path, self.get_transform()) - # MGDTODO: If _draw_steps is removed, remove the following line also - self._step_path = None + def set_transform(self, t): """ set the Transformation instance used by this artist @@ -434,47 +433,8 @@ if len(x)<2: return 1 return npy.alltrue(x[1:]-x[0:-1]>=0) - # MGDTODO: Remove me (seems to be used for old-style interface only) - def _get_plottable(self): - # If log scale is set, only pos data will be returned - - x, y = self._x, self._y - - # MGDTODO: (log-scaling) - -# try: logx = self.get_transform().get_funcx().get_type()==LOG10 -# except RuntimeError: logx = False # non-separable - -# try: logy = self.get_transform().get_funcy().get_type()==LOG10 -# except RuntimeError: logy = False # non-separable - - if True: - return x, y - - if self._logcache is not None: - waslogx, waslogy, xcache, ycache = self._logcache - if logx==waslogx and waslogy==logy: - return xcache, ycache - - Nx = len(x) - Ny = len(y) - - if logx: indx = npy.greater(x, 0) - else: indx = npy.ones(len(x)) - - if logy: indy = npy.greater(y, 0) - else: indy = npy.ones(len(y)) - - ind, = npy.nonzero(npy.logical_and(indx, indy)) - x = npy.take(x, ind) - y = npy.take(y, ind) - - self._logcache = logx, logy, x, y - return x, y - - def draw(self, renderer): - #renderer.open_group('line2d') + renderer.open_group('line2d') if not self._visible: return self._newstyle = hasattr(renderer, 'draw_markers') @@ -498,7 +458,6 @@ lineFunc = getattr(self, funcname) lineFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) - # MGDTODO: Deal with markers if self._marker is not None: gc = renderer.new_gc() self._set_gc_clip(gc) @@ -509,7 +468,7 @@ markerFunc = getattr(self, funcname) markerFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) - #renderer.close_group('line2d') + renderer.close_group('line2d') def get_antialiased(self): return self._antialiased def get_color(self): return self._color @@ -694,29 +653,7 @@ def _draw_nothing(self, *args, **kwargs): pass - - def _draw_steps(self, renderer, gc, path): - # We generate the step function path on-the-fly, and then cache it. - # The cache may be later invalidated when the data changes - # (in self.recache()) - - # MGDTODO: Untested -- using pylab.step doesn't actually trigger - # this code -- the path is "stepped" before even getting to this - # class. Perhaps this should be removed here, since it is not as - # powerful as what is in axes.step() anyway. - if self._step_path is None: - vertices = self._path.vertices - codes = self._path.codes - siz = len(vertices) - if siz<2: return - new_vertices = npy.zeros((2*siz, 2), vertices.dtype) - new_vertices[0:-1:2, 0], new_vertices[1:-1:2, 0], newvertices[-1, 0] = vertices[:, 0], vertices[1:, 0], vertices[-1, 0] - new_vertices[0:-1:2, 1], new_vertices[1::2, 1] = vertices[:, 1], vertices[:, 1] - self._step_path = Path(new_vertices, closed=False) - gc.set_linestyle('solid') - renderer.draw_path(gc, self._step_path, self.get_transform()) - - + def _draw_solid(self, renderer, gc, path, trans): gc.set_linestyle('solid') renderer.draw_path(gc, path, trans) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/patches.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -12,8 +12,6 @@ import matplotlib.mlab as mlab import matplotlib.artist as artist from matplotlib.path import Path -# MGDTODO: Maybe this belongs elsewhere -from matplotlib.backends._backend_agg import point_in_path # these are not available for the object inspector until after the # class is build so we define an initial set here for the init @@ -93,8 +91,8 @@ # method. if callable(self._contains): return self._contains(self,mouseevent) - inside = point_in_path(mouseevent.x, mouseevent.y, self.get_path(), - self.get_transform().frozen()) + inside = self.get_path().contains_point( + (mouseevent.x, mouseevent.y), self.get_transform()) return inside, {} def update_from(self, other): @@ -236,8 +234,8 @@ def get_window_extent(self, renderer=None): - trans_path = self.get_path().transformed(self.get_path_transform()) - return Bbox.unit().update_from_data(trans_path.vertices) + return Bbox.from_lbrt( + get_path_extents(self.get_path(), self.get_patch_transform())) def set_lw(self, val): @@ -341,7 +339,9 @@ Patch.__init__(self, **kwargs) - self._bbox = transforms.Bbox.from_lbwh(xy[0], xy[1], width, height) + left, right = self.convert_xunits((xy[0], xy[0] + width)) + bottom, top = self.convert_yunits((xy[1], xy[1] + height)) + self._bbox = transforms.Bbox.from_lbrt(left, bottom, right, top) self._rect_transform = transforms.BboxTransform( transforms.Bbox.unit(), self._bbox) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -352,10 +352,6 @@ """ return Path.unit_rectangle() - # MGDTODO: Convert units -# left, right = self.convert_xunits((x, x + self.width)) -# bottom, top = self.convert_yunits((y, y + self.height)) - def get_patch_transform(self): return self._rect_transform Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/path.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -1,3 +1,9 @@ +""" +Contains a class for managing paths (polylines). + +October 2007 Michael Droettboom +""" + import math import numpy as npy @@ -3,7 +9,47 @@ from numpy import ma as ma +from matplotlib.backends._backend_agg import point_in_path, get_path_extents +from matplotlib.cbook import simple_linear_interpolation + KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0 class Path(object): + """ + Path represents a series of possibly disconnected, possibly + closed, line and curve segments. + + The underlying storage is made up of two parallel numpy arrays: + vertices: an Nx2 float array of vertices + codes: an N-length uint8 array of vertex types + + These two arrays always have the same length in the first + dimension. Therefore, to represent a cubic curve, you must + provide three vertices as well as three codes "CURVE3". + + The code types are: + + STOP : 1 vertex (ignored) + A marker for the end of the entire path (currently not + required and ignored) + + MOVETO : 1 vertex + Pick up the pen and move to the given vertex. + + LINETO : 1 vertex + Draw a line from the current position to the given vertex. + + CURVE3 : 1 control point, 1 endpoint + Draw a quadratic Bezier curve from the current position, + with the given control point, to the given end point. + + CURVE4 : 2 control points, 1 endpoint + Draw a cubic Bezier curve from the current position, with + the given control points, to the given end point. + + CLOSEPOLY : 1 vertex (ignored) + Draw a line segment to the start point of the current + polyline. + """ + # Path codes STOP = 0 # 1 vertex @@ -13,19 +59,31 @@ CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices CLOSEPOLY = 5 # 1 vertex - ### - # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, - # so if they don't, we probably shouldn't - CURVEN = 6 - CATROM = 7 - UBSPLINE = 8 - #### NUM_VERTICES = [1, 1, 1, 2, 3, 1] code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): + """ + Create a new path with the given vertices and codes. + + vertices is an Nx2 numpy float array. + + codes is an N-length numpy array of type Path.code_type. + + See the docstring of Path for a description of the various + codes. + + These two arrays must have the same length in the first + dimension. + + If codes is None, vertices will be treated as a series of line + segments. Additionally, if closed is also True, the polyline + will closed. If vertices contains masked values, the + resulting path will be compressed, with MOVETO codes inserted + in the correct places to jump over the masked regions. + """ vertices = ma.asarray(vertices, npy.float_) if codes is None: @@ -82,6 +140,11 @@ vertices = property(_get_vertices) def iter_endpoints(self): + """ + Iterates over all of the endpoints in the path. Unlike + iterating directly over the vertices array, curve control + points are skipped over. + """ i = 0 NUM_VERTICES = self.NUM_VERTICES vertices = self.vertices @@ -95,11 +158,57 @@ i += 1 def transformed(self, transform): + """ + Return a transformed copy of the path. + + See transforms.TransformedPath for a path that will cache the + transformed result and automatically update when the transform + changes. + """ return Path(transform.transform(self.vertices), self.codes) - + + def contains_point(self, point, transform=None): + """ + Returns True if the path contains the given point. + + If transform is not None, the path will be transformed before + performing the test. + """ + if transform is None: + from transforms import IdentityTransform + transform = IdentityTransform + return point_in_path(point[0], point[1], self, transform.frozen()) + + def get_extents(self, transform=None): + """ + Returns the extents (xmin, ymin, xmax, ymax) of the path. + + Unlike computing the extents on the vertices alone, this + algorithm will take into account the curves and deal with + control points appropriately. + """ + from transforms import Bbox, IdentityTransform + if transform is None: + transform = IdentityTransform + return Bbox.from_lbrt(*get_path_extents(self, transform)) + + def interpolated(self, steps): + """ + Returns a new path resampled to length N x steps. + Does not currently handle interpolating curves. + """ + vertices = simple_linear_interpolation(self.vertices, steps) + codes = self.codes + new_codes = Path.LINETO * npy.ones(((len(codes) - 1) * steps + 1, )) + new_codes[0::steps] = codes + return Path(vertices, new_codes) + _unit_rectangle = None #@classmethod def unit_rectangle(cls): + """ + Returns a Path of the unit rectangle from (0, 0) to (1, 1). + """ if cls._unit_rectangle is None: cls._unit_rectangle = \ Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) @@ -109,9 +218,14 @@ _unit_regular_polygons = {} #@classmethod def unit_regular_polygon(cls, numVertices): + """ + Returns a Path for a unit regular polygon with the given + numVertices and radius of 1.0, centered at (0, 0). + """ path = cls._unit_regular_polygons.get(numVertices) if path is None: - theta = 2*npy.pi/numVertices * npy.arange(numVertices).reshape((numVertices, 1)) + theta = (2*npy.pi/numVertices * + npy.arange(numVertices).reshape((numVertices, 1))) # This initial rotation is to make sure the polygon always # "points-up" theta += npy.pi / 2.0 @@ -124,6 +238,10 @@ _unit_circle = None #@classmethod def unit_circle(cls): + """ + Returns a Path of the unit circle. The circle is approximated + using cubic Bezier curves. + """ if cls._unit_circle is None: offset = KAPPA vertices = npy.array( @@ -158,6 +276,10 @@ #@classmethod def arc(cls, theta1, theta2, is_wedge=False): + """ + Returns an arc on the unit circle from angle theta1 to angle + theta2 (in degrees). + """ # From Masionobe, L. 2003. "Drawing an elliptical arc using # polylines, quadratic or cubic Bezier curves". # @@ -234,5 +356,9 @@ arc = classmethod(arc) def wedge(cls, theta1, theta2): + """ + Returns a wedge of the unit circle from angle theta1 to angle + theta2 (in degrees). + """ return cls.arc(theta1, theta2, True) wedge = classmethod(wedge) Modified: branches/transforms/lib/matplotlib/projections/polar.py =================================================================== --- branches/transforms/lib/matplotlib/projections/polar.py 2007-10-03 23:05:30 UTC (rev 3911) +++ branches/transforms/lib/matplotlib/projections/polar.py 2007-10-04 17:21:26 UTC (rev 3912) @@ -2,9 +2,12 @@ import numpy as npy +from matplotlib.artist import kwdocd from matplotlib.axes import Axes +from matplotlib import cbook from matplotlib.patches import Circle -from matplotlib.ticker import Locator +from matplotlib.path import Path +from matplotlib.ticker import Formatter, Locator from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, BboxTransform, \ IdentityTransform, Transform, TransformWrapper @@ -16,6 +19,10 @@ output_dims = 2 is_separable = False + def __init__(self, resolution): + Transform.__init__(self) + self._resolution = resolution + def transform(self, tr): xy = npy.zeros(tr.shape, npy.float_) t = tr[:, 0:1] @@ -27,6 +34,12 @@ return xy transform_non_affine = transform + def transform_path(self, path): + if len(path.vertices) == 2: + path = path.interpolated(self._resolution) + return Path(self.transform(path.vertices), path.codes) + transform_path_non_affine = transform_path + def inverted(self): return PolarAxes.InvertedPolarTransform() @@ -64,10 +77,35 @@ def inverted(self): return PolarAxes.PolarTransform() - class ThetaLocator(Locator): - pass + class ThetaFormatter(Formatter): + def __call__(self, x, pos=None): + return u"%d\u00b0" % ((x / npy.pi) * 180.0) + + class RadialLocator(Locator): + def __init__(self, base): + self.base = base + + def __call__(self): + ticks = self.base() + # MGDTODO: Use numpy + return [x for x in ticks if x > 0] + + def autoscale(self): + return self.base.autoscale() + + def pan(self, numsteps): + return self.base.pan(numsteps) + + def zoom(self, direction): + return self.base.zoom(direction) + + def refresh(self): + return self.base.refresh() + + RESOLUTION = 100 def __init__(self, *args, **kwargs): + self._rpad = 0.05 Axes.__init__(self, *args, **kwargs) self.set_aspect('equal', adjustable='box', anchor='C') self.cla() @@ -76,55 +114,105 @@ def cla(self): Axes.cla(self) - self.xaxis.set_major_locator(PolarAxes.ThetaLocator()) - - def _set_transData(self): + self.xaxis.set_major_formatter(self.ThetaFormatter()) + angles = npy.arange(0.0, 360.0, 45.0) + self.set_thetagrids(angles) + self.yaxis.set_major_locator(self.RadialLocator(self.yaxis.get_major_locator())) + + def _set_lim_and_transforms(self): + self.dataLim = Bbox([[0.0, 0.0], [npy.pi * 2.0, 1.0]]) + self.viewLim = Bbox.unit() + self.transAxes = BboxTransform(Bbox.unit(), self.bbox) + + # Transforms the x and y axis separately by a scale factor + # It is assumed that this part will have non-linear components + self.transScale = TransformWrapper(IdentityTransform()) + # A (possibly non-linear) projection on the (already scaled) data - self.transProjection = self.PolarTransform() + self.transProjection = self.PolarTransform(self.RESOLUTION) # An affine transformation on the data, generally to limit the # range of the axes self.transProjectionAffine = self.PolarAffine(self.viewLim) self.transData = self.transScale + self.transProjection + \ - self.transProjectionAffine + self.transAxes + (self.transProjectionAffine + self.transAxes) self._xaxis_transform = ( - self.PolarTransform() + + self.transProjection + self.PolarAffine(Bbox.unit()) + self.transAxes) + self._theta_label1_position = Affine2D().translate(0.0, 1.1) + self._xaxis_text1_transform = ( + self._theta_label1_positi... [truncated message content] |