From: <md...@us...> - 2007-10-03 12:50:11
|
Revision: 3908 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3908&view=rev Author: mdboom Date: 2007-10-03 05:50:04 -0700 (Wed, 03 Oct 2007) Log Message: ----------- Lots of progress on Polar transform refactoring. Added point_in_path algorithm. Modified Paths: -------------- 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/cbook.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.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/setup.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Added Paths: ----------- branches/transforms/lib/matplotlib/projections/ branches/transforms/lib/matplotlib/projections/polar.py Removed Paths: ------------- branches/transforms/lib/matplotlib/pbox.py Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/artist.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -1,7 +1,8 @@ from __future__ import division import sys, re from cbook import iterable, flatten -from transforms import Affine2D +from transforms import Affine2D, Bbox, IdentityTransform, TransformedBbox, \ + TransformedPath import matplotlib.units as units ## Note, matplotlib artists use the doc strings for set and get @@ -145,7 +146,7 @@ def get_transform(self): 'return the Transformation instance used by this artist' if self._transform is None: - self._transform = Affine2D() + self._transform = IdentityTransform() return self._transform def hitlist(self,event): @@ -284,17 +285,29 @@ self._clipon = clipbox is not None or self._clippath is not None self.pchanged() - def set_clip_path(self, path): + def set_clip_path(self, path, transform=None): """ Set the artist's clip path - ACCEPTS: an agg.path_storage instance + ACCEPTS: a Path instance and a Transform instance, or a Patch instance """ - self._clippath = path + from patches import Patch, Rectangle + if transform is None: + if isinstance(path, Rectangle): + self.clipbox = TransformedBbox(Bbox.unit(), path.get_transform()) + elif isinstance(path, Patch): + self._clippath = TransformedPath( + path.get_path(), + path.get_transform()) + elif path is None: + self._clippath = None + else: + raise TypeError("Invalid arguments to set_clip_path") + else: + self._clippath = TransformedPath(path, transform) self._clipon = self.clipbox is not None or path is not None self.pchanged() - def get_alpha(self): """ Return the alpha value used for blending - not supported on all @@ -431,6 +444,7 @@ self._alpha = other._alpha self.clipbox = other.clipbox self._clipon = other._clipon + self._clippath = other._clippath self._lod = other._lod self._label = other._label self.pchanged() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -1,5 +1,5 @@ from __future__ import division, generators -import math, sys, warnings, copy +import math, sys, warnings, copy, new import numpy as npy @@ -24,7 +24,6 @@ from matplotlib import cm from matplotlib import patches as mpatches from matplotlib import path as mpath -from matplotlib import pbox as mpbox from matplotlib import quiver as mquiver from matplotlib import scale as mscale from matplotlib import table as mtable @@ -428,15 +427,8 @@ """ - - # MGDTODO -# scaled = {mtrans.IDENTITY : 'linear', -# mtrans.LOG10 : 'log', -# } - scaled = {0 : 'linear', - 1 : 'log', - } - + name = "rectilinear" + _shared_x_axes = cbook.Grouper() _shared_y_axes = cbook.Grouper() @@ -474,16 +466,16 @@ visible: a boolean - whether the axes is visible xlabel: the xlabel xlim: (xmin, xmax) view limits - xscale: ['log' | 'linear' ] + xscale: [%(scale)s] xticklabels: sequence of strings xticks: sequence of floats ylabel: the ylabel strings ylim: (ymin, ymax) view limits - yscale: ['log' | 'linear'] + yscale: [%(scale)s] yticklabels: sequence of strings yticks: sequence of floats - """ + """ % {'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) @@ -637,6 +629,10 @@ # 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() @@ -647,7 +643,33 @@ self.transData = self.transScale + self.transProjection + self.transLimits + self.transAxes + self._xaxis_transform = mtransforms.blended_transform_factory( + self.axes.transData, self.axes.transAxes) + self._yaxis_transform = mtransforms.blended_transform_factory( + self.axes.transAxes, self.axes.transData) + 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)) + + def get_xaxis_text2_transform(self, pad_pixels): + return (self._xaxis_transform + + mtransforms.Affine2D().translate(0, pad_pixels)) + + 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)) + + def get_yaxis_text2_transform(self, pad_pixels): + return (self._yaxis_transform + + mtransforms.Affine2D().translate(pad_pixels, 0)) + def _update_transScale(self): self.transScale.set( mtransforms.blended_transform_factory( @@ -691,6 +713,9 @@ a.set_transform(self.transData) a.axes = self + def get_axes_patch(self): + return mpatches.Rectangle((0.0, 0.0), 1.0, 1.0) + def cla(self): 'Clear the current axes' @@ -734,24 +759,26 @@ self.title.set_clip_box(None) self._set_artist_props(self.title) - - self.axesPatch = mpatches.Rectangle( - xy=(0,0), width=1, height=1, - facecolor=self._axisbg, - edgecolor=rcParams['axes.edgecolor'], - ) + + self.axesPatch = self.get_axes_patch() self.axesPatch.set_figure(self.figure) - self.axesPatch.set_transform(self.transAxes) + self.axesPatch.set_facecolor(self._axisbg) + self.axesPatch.set_edgecolor(rcParams['axes.edgecolor']) self.axesPatch.set_linewidth(rcParams['axes.linewidth']) - # MGDTODO: What is axesFrame for? We already have axesPatch - self.axesFrame = mlines.Line2D((0,1,1,0,0), (0,0,1,1,0), - linewidth=rcParams['axes.linewidth'], - color=rcParams['axes.edgecolor'], - figure=self.figure) + self.axesPatch.set_transform(self.transAxes) + + self.axesFrame = self.get_axes_patch() + self.axesFrame.set_figure(self.figure) + self.axesFrame.set_facecolor(None) + self.axesFrame.set_edgecolor(rcParams['axes.edgecolor']) + self.axesFrame.set_linewidth(rcParams['axes.linewidth']) self.axesFrame.set_transform(self.transAxes) self.axesFrame.set_zorder(2.5) self.axison = True + self.xaxis.set_clip_path(self.axesPatch) + self.yaxis.set_clip_path(self.axesPatch) + def clear(self): 'clear the axes' self.cla() @@ -838,19 +865,23 @@ """ ACCEPTS: ['C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'] """ - if anchor in mpbox.PBox.coefs.keys() or len(anchor) == 2: + if anchor in mtransforms.Bbox.coefs.keys() or len(anchor) == 2: self._anchor = anchor else: raise ValueError('argument must be among %s' % - ', '.join(PBox.coefs.keys())) + ', '.join(mtransforms.BBox.coefs.keys())) - - def apply_aspect(self, data_ratio = None): + def get_data_ratio(self): + 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) + return ysize/xsize + + def apply_aspect(self): ''' Use self._aspect and self._adjustable to modify the axes box or the view limits. - The data_ratio kwarg is set to 1 for polar axes. It is - used only when _adjustable is 'box'. ''' if self._aspect == 'auto': @@ -869,18 +900,11 @@ 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() - xsize = max(math.fabs(xmax-xmin), 1e-30) - ymin,ymax = self.get_ylim() - ysize = max(math.fabs(ymax-ymin), 1e-30) if self._adjustable == 'box': - if data_ratio is None: - data_ratio = ysize/xsize - box_aspect = A * data_ratio - pb = mpbox.PBox(self._originalPosition) - pb1 = pb.shrink_to_aspect(box_aspect, fig_aspect) - self.set_position(pb1.anchor(self._anchor), 'active') + box_aspect = A * self.get_data_ratio() + pb = self._originalPosition.frozen() + pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect) + self.set_position(pb1.anchored(self._anchor, pb), 'active') return @@ -1079,7 +1103,7 @@ collection.set_label('collection%d'%len(self.collections)) self.collections.append(collection) self._set_artist_props(collection) - collection.set_clip_box(self.bbox) + collection.set_clip_path(self.axesPatch) if autolim: self.update_datalim(collection.get_verts(self.transData)) collection._remove_method = lambda h: self.collections.remove(h) @@ -1087,7 +1111,7 @@ def add_line(self, line): 'Add a line to the list of plot lines' self._set_artist_props(line) - line.set_clip_box(self.bbox) + line.set_clip_path(self.axesPatch) self._update_line_limits(line) if not line.get_label(): @@ -1107,7 +1131,7 @@ """ self._set_artist_props(p) - p.set_clip_box(self.bbox) + p.set_clip_path(self.axesPatch) self._update_patch_limits(p) self.patches.append(p) p._remove_method = lambda h: self.patches.remove(h) @@ -1115,7 +1139,7 @@ def _update_patch_limits(self, p): 'update the datalimits for patch p' xys = self._get_verts_in_data_coords( - p.get_transform(), p.get_verts()) + p.get_transform(), p.get_path().vertices) self.update_datalim(xys) @@ -1160,8 +1184,8 @@ # display and then back to data to get it in data units #xys = trans.seq_xy_tups(xys) #return [ self.transData.inverse_xy_tup(xy) for xy in xys] - xys = trans(npy.asarray(xys)) - return self.transData.inverted()(xys) + xys = trans.transform(npy.asarray(xys)) + return self.transData.inverted().transform(xys) def _process_unit_info(self, xdata=None, ydata=None, kwargs=None): 'look for unit kwargs and update the axis instances as necessary' @@ -1189,9 +1213,9 @@ #print '\tkw setting yunits', yunits self.yaxis.set_units(yunits) - def in_axes(self, xwin, ywin): + def in_axes(self, mouseevent): 'return True is the point xwin, ywin (display coords) are in the Axes' - return self.bbox.contains(xwin, ywin) + return self.axesPatch.contains(mouseevent)[0] def get_autoscale_on(self): """ @@ -1207,7 +1231,6 @@ """ self._autoscaleon = b - def autoscale_view(self, tight=False, scalex=True, scaley=True): """ autoscale the view limits using the data limits. You can @@ -1239,8 +1262,8 @@ if yl[1] < yl[0]: YL = YL[::-1] self.set_ylim(YL) - #### Drawing + #### Drawing def draw(self, renderer=None, inframe=False): "Draw everything (plot lines, axes, labels)" if renderer is None: @@ -1252,7 +1275,9 @@ renderer.open_group('axes') self.apply_aspect() - if self.axison and self._frameon: self.axesPatch.draw(renderer) + if self.axison and self._frameon: + self.axesPatch.draw(renderer) + artists = [] if len(self.images)<=1 or renderer.option_image_nocomposite(): @@ -1262,7 +1287,6 @@ # make a composite image blending alpha # list of (mimage.Image, ox, oy) - mag = renderer.get_image_magnification() ims = [(im.make_image(mag),0,0) for im in self.images if im.get_visible()] @@ -1277,8 +1301,6 @@ # respect z-order for now renderer.draw_image(l, b, im, self.bbox) - - artists.extend(self.collections) artists.extend(self.patches) artists.extend(self.lines) @@ -1517,14 +1539,15 @@ return xmin, xmax def get_xscale(self): - 'return the xaxis scale string: log or linear' + 'return the xaxis scale string: %s' % ( + ", ".join(mscale.get_scale_names())) return self.xaxis.get_scale() def set_xscale(self, value, **kwargs): """ - SET_XSCALE(value, basex=10, subsx=None) + SET_XSCALE(value) - Set the xscaling: 'log' or 'linear' + Set the xscaling: %(scale)s If value is 'log', the additional kwargs have the following meaning @@ -1536,8 +1559,8 @@ put minor ticks on 1,2,5,11,12,15,21, ....To turn off minor ticking, set subsx=[] - ACCEPTS: ['log' | 'linear' ] - """ + ACCEPTS: [%(scale)s] + """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} self.xaxis.set_scale(value, **kwargs) self._update_transScale() @@ -1610,11 +1633,6 @@ if ymin is None: ymin = old_ymin if ymax is None: ymax = old_ymax - # MGDTODO -# if (self.transData.get_funcy().get_type()==mtrans.LOG10 -# and min(ymin, ymax)<=0): -# raise ValueError('Cannot set nonpositive limits with log transform') - ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) self.viewLim.intervaly = (ymin, ymax) if emit: @@ -1627,14 +1645,15 @@ return ymin, ymax def get_yscale(self): - 'return the yaxis scale string: log or linear' + 'return the xaxis scale string: %s' % ( + ", ".join(mscale.get_scale_names())) return self.yaxis.get_scale() def set_yscale(self, value, **kwargs): """ SET_YSCALE(value, basey=10, subsy=None) - Set the yscaling: 'log' or 'linear' + Set the yscaling: %(scale)s If value is 'log', the additional kwargs have the following meaning @@ -1646,8 +1665,8 @@ put minor ticks on 1,2,5,11,12,15, 21, ....To turn off minor ticking, set subsy=[] - ACCEPTS: ['log' | 'linear'] - """ + ACCEPTS: %(scale)s + """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} self.yaxis.set_scale(value, **kwargs) self._update_transScale() @@ -1682,14 +1701,6 @@ return self.yaxis.set_ticklabels(labels, fontdict, **kwargs) set_yticklabels.__doc__ = cbook.dedent(set_yticklabels.__doc__) % martist.kwdocd - def toggle_log_lineary(self): - 'toggle between log and linear on the y axis' - # MGDTODO -# funcy = self.transData.get_funcy().get_type() -# if funcy==mtrans.LOG10: self.set_yscale('linear') -# elif funcy==mtrans.IDENTITY: self.set_yscale('log') - pass - def xaxis_date(self, tz=None): """Sets up x-axis ticks and labels that treat the x data as dates. @@ -1748,12 +1759,12 @@ def format_coord(self, x, y): 'return a format string formatting the x, y coord' - + if x is None or y is None: + return '' xs = self.format_xdata(x) ys = self.format_ydata(y) return 'x=%s, y=%s'%(xs,ys) - - + #### Interactive manipulation def get_navigate(self): @@ -1784,17 +1795,40 @@ """ self._navigate_mode = b - def drag_pan(self, button, x, y, startx, starty, start_lim, start_trans): + def drag_pan(self, button, key, startx, starty, dx, dy, + start_lim, start_trans): + def format_deltas(key, dx, dy): + if key=='control': + if(abs(dx)>abs(dy)): + dy = dx + else: + dx = dy + elif key=='x': + dy = 0 + elif key=='y': + dx = 0 + elif key=='shift': + if 2*abs(dx) < abs(dy): + dx=0 + elif 2*abs(dy) < abs(dx): + dy=0 + elif(abs(dx)>abs(dy)): + dy=dy/abs(dy)*abs(dx) + else: + dx=dx/abs(dx)*abs(dy) + return (dx,dy) + if button == 1: inverse = start_trans.inverted() - dx = startx - x - dy = starty - y - result = self.bbox.frozen().translated(dx, dy).transformed(inverse) + dx, dy = format_deltas(key, dx, dy) + result = self.bbox.frozen().translated(-dx, -dy).transformed(inverse) elif button == 3: try: + # MGDTODO: This is broken with log scales inverse = start_trans.inverted() - dx = (startx - x) / float(self.bbox.width) - dy = (starty - y) / float(self.bbox.height) + 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 alpha = npy.power(10.0, (dx, dy)) @@ -1806,7 +1840,6 @@ warnings.warn('Overflow while panning') return - # MGDTODO: Could we do this with a single set_lim? self.set_xlim(*result.intervalx) self.set_ylim(*result.intervaly) @@ -1880,7 +1913,7 @@ """ if callable(self._contains): return self._contains(self,mouseevent) - inside = self.bbox.contains(mouseevent.x,mouseevent.y) + inside = self.axesPatch.contains(mouseevent.x, mouseevent.y) return inside,{} def pick(self,*args): @@ -2100,7 +2133,7 @@ #if t.get_clip_on(): t.set_clip_box(self.bbox) - if kwargs.has_key('clip_on'): t.set_clip_box(self.bbox) + if kwargs.has_key('clip_on'): t.set_clip_path(self.axesPatch) return t text.__doc__ = cbook.dedent(text.__doc__) % martist.kwdocd @@ -2118,7 +2151,7 @@ a = mtext.Annotation(*args, **kwargs) a.set_transform(mtransforms.Affine2D()) self._set_artist_props(a) - if kwargs.has_key('clip_on'): a.set_clip_box(self.bbox) + if kwargs.has_key('clip_on'): a.set_clip_path(self.axesPatch) self.texts.append(a) return a annotate.__doc__ = cbook.dedent(annotate.__doc__) % martist.kwdocd @@ -5056,7 +5089,7 @@ Subplot(211) # 2 rows, 1 column, first (upper) plot """ - def __init__(self, fig, *args): + def __init__(self, fig, *args, **kwargs): """ fig is a figure instance @@ -5087,6 +5120,10 @@ 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) + def get_geometry(self): 'get the subplot geometry, eg 2,2,3' return self._rows, self._cols, self._num+1 @@ -5176,36 +5213,18 @@ for label in self.get_yticklabels(): label.set_visible(firstcol) -class Subplot(SubplotBase, Axes): - """ - Emulate matlab's(TM) subplot command, creating axes with +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 + new_class = new.classobj("%sSubplot" % (axes_class.__name__), + (SubplotBase, axes_class), + {'_axes_class': axes_class}) + return new_class - Subplot(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 "Subplot(%f,%f,%f,%f)" % (self.bbox.bounds) - - def __init__(self, fig, *args, **kwargs): - """ - See Axes base class documentation for args and kwargs - """ - SubplotBase.__init__(self, fig, *args) - Axes.__init__(self, fig, [self.figLeft, self.figBottom, - self.figW, self.figH], **kwargs) - - - class PolarAxes(Axes): """ - Make a PolarAxes. The rectangular bounding box of the axes is given by @@ -5644,11 +5663,6 @@ 'return the yaxis scale string' return 'polar' - def toggle_log_lineary(self): - 'toggle between log and linear axes ignored for polar' - pass - - def table(self, *args, **kwargs): """ TABLE(*args, **kwargs) @@ -5679,255 +5693,9 @@ PolarAxes.__init__( self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) - -###################################################################### -# New Polar Axes -class PolarAxes(Axes): - class PolarTransform(mtransforms.Transform): - input_dims = 2 - output_dims = 2 - is_separable = False - - def transform(self, tr): - xy = npy.zeros(tr.shape, npy.float_) - t = tr[:, 0:1] - r = tr[:, 1:2] - x = xy[:, 0:1] - y = xy[:, 1:2] - x += r * npy.cos(t) - y += r * npy.sin(t) - return xy - transform_non_affine = transform - - def interpolate(self, a, steps): - steps = npy.floor(steps) - new_length = ((len(a) - 1) * steps) + 1 - new_shape = list(a.shape) - new_shape[0] = new_length - result = npy.zeros(new_shape, a.dtype) - - result[0] = a[0] - a0 = a[0:-1] - a1 = a[1: ] - delta = ((a1 - a0) / steps) - - for i in range(1, int(steps)+1): - result[i::steps] = delta * i + a0 - - return result - -# def transform_path(self, path): -# twopi = 2.0 * npy.pi -# halfpi = 0.5 * npy.pi - -# vertices = path.vertices -# t0 = vertices[0:-1, 0] -# t1 = vertices[1: , 0] -# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) -# maxtd = td.max() -# interpolate = npy.ceil(maxtd / halfpi) -# if interpolate > 1.0: -# vertices = self.interpolate(vertices, interpolate) - -# vertices = self.transform(vertices) - -# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_) -# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) -# result[0] = vertices[0] -# codes[0] = mpath.Path.MOVETO - -# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0) -# kappa = 0.5 - -# p0 = vertices[0:-1] -# p1 = vertices[1: ] - -# x0 = p0[:, 0:1] -# y0 = p0[:, 1: ] -# b0 = ((y0 - x0) - y0) / ((x0 + y0) - x0) -# a0 = y0 - b0*x0 - -# x1 = p1[:, 0:1] -# y1 = p1[:, 1: ] -# b1 = ((y1 - x1) - y1) / ((x1 + y1) - x1) -# a1 = y1 - b1*x1 - -# x = -(a0-a1) / (b0-b1) -# y = a0 + b0*x - -# xk = (x - x0) * kappa + x0 -# yk = (y - y0) * kappa + y0 - -# result[1::3, 0:1] = xk -# result[1::3, 1: ] = yk - -# xk = (x - x1) * kappa + x1 -# yk = (y - y1) * kappa + y1 - -# result[2::3, 0:1] = xk -# result[2::3, 1: ] = yk - -# result[3::3] = p1 - -# print vertices[-2:] -# print result[-2:] - -# return mpath.Path(result, codes) - -# twopi = 2.0 * npy.pi -# halfpi = 0.5 * npy.pi - -# vertices = path.vertices -# t0 = vertices[0:-1, 0] -# t1 = vertices[1: , 0] -# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) -# maxtd = td.max() -# interpolate = npy.ceil(maxtd / halfpi) - -# print "interpolate", interpolate -# if interpolate > 1.0: -# vertices = self.interpolate(vertices, interpolate) - -# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_) -# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) -# result[0] = vertices[0] -# codes[0] = mpath.Path.MOVETO - -# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0) -# tkappa = npy.arctan(kappa) -# hyp_kappa = npy.sqrt(kappa*kappa + 1.0) - -# t0 = vertices[0:-1, 0] -# t1 = vertices[1: , 0] -# r0 = vertices[0:-1, 1] -# r1 = vertices[1: , 1] - -# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) -# td_scaled = td / (npy.pi * 0.5) -# rd = r1 - r0 -# r0kappa = r0 * kappa * td_scaled -# r1kappa = r1 * kappa * td_scaled -# ravg_kappa = ((r1 + r0) / 2.0) * kappa * td_scaled - -# result[1::3, 0] = t0 + (tkappa * td_scaled) -# result[1::3, 1] = r0*hyp_kappa -# # result[1::3, 1] = r0 / npy.cos(tkappa * td_scaled) # npy.sqrt(r0*r0 + ravg_kappa*ravg_kappa) - -# result[2::3, 0] = t1 - (tkappa * td_scaled) -# result[2::3, 1] = r1*hyp_kappa -# # result[2::3, 1] = r1 / npy.cos(tkappa * td_scaled) # npy.sqrt(r1*r1 + ravg_kappa*ravg_kappa) - -# result[3::3, 0] = t1 -# result[3::3, 1] = r1 - -# print vertices[:6], result[:6], t0[:6], t1[:6], td[:6], td_scaled[:6], tkappa -# result = self.transform(result) -# return mpath.Path(result, codes) -# transform_path_non_affine = transform_path - - def inverted(self): - return PolarAxes.InvertedPolarTransform() - - class PolarAffine(mtransforms.Affine2DBase): - def __init__(self, limits): - mtransforms.Affine2DBase.__init__(self) - self._limits = limits - self.set_children(limits) - self._mtx = None - - def get_matrix(self): - if self._invalid: - xmin, ymin, xmax, ymax = self._limits.lbrt - affine = mtransforms.Affine2D().rotate(xmin).scale(0.5 / ymax).translate(0.5, 0.5) - self._mtx = affine.get_matrix() - self._inverted = None - self._invalid = 0 - return self._mtx - - class InvertedPolarTransform(mtransforms.Transform): - input_dims = 2 - output_dims = 2 - is_separable = False - - def transform(self, xy): - x = xy[:, 0:1] - y = xy[:, 1:] - r = npy.sqrt(x*x + y*y) - theta = npy.arccos(x / r) - theta = npy.where(y < 0, 2 * npy.pi - theta, theta) - return npy.concatenate((theta, r), 1) - - def inverted(self): - return PolarAxes.PolarTransform() - - def _set_lim_and_transforms(self): - """ - set the dataLim and viewLim BBox attributes and the - transData and transAxes Transformation attributes - """ - self.dataLim = mtransforms.Bbox.unit() - self.viewLim = mtransforms.Bbox.unit() - self.transAxes = mtransforms.BboxTransform( - mtransforms.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 = mtransforms.TransformWrapper(mtransforms.IdentityTransform()) - - # A (possibly non-linear) projection on the (already scaled) data - self.transProjection = self.PolarTransform() - - # 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 - - def drag_pan(self, button, x, y, startx, starty, start_lim, start_trans): - if button == 1: - inverse = start_trans.inverted() - startt, startr = inverse.transform_point((startx, starty)) - t, r = inverse.transform_point((x, y)) - - scale = r / startr - self.set_ylim(start_lim.ymin, start_lim.ymax / scale) - - dt0 = t - startt - dt1 = startt - t - if abs(dt1) < abs(dt0): - dt = abs(dt1) * sign(dt0) * -1.0 - else: - dt = dt0 * -1.0 - self.set_xlim(start_lim.xmin - dt, start_lim.xmin - dt + npy.pi*2.0) - - def set_rmax(self, rmax): - self.viewLim.maxy = rmax - -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) + """ # this is some discarded code I was using to find the minimum positive # data point for some log scaling fixes. I realized there was a Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -18,12 +18,11 @@ from font_manager import FontProperties from text import Text, TextWithDash, _process_text_args from transforms import Affine2D, Bbox, blended_transform_factory, interval_contains, \ - interval_contains_open, IntervalTransform + interval_contains_open, IntervalTransform, IdentityTransform from patches import bbox_artist from scale import scale_factory import matplotlib.units as units -#import pdb class Tick(Artist): @@ -112,6 +111,12 @@ children = [self.tick1line, self.tick2line, self.gridline, self.label1, self.label2] return children + def set_clip_path(self, clippath, transform=None): + Artist.set_clip_path(self, clippath, transform) + self.tick1line.set_clip_path(clippath, transform) + self.tick2line.set_clip_path(clippath, transform) + self.gridline.set_clip_path(clippath, transform) + def contains(self, mouseevent): """Test whether the mouse event occured in the Tick marks. @@ -164,9 +169,12 @@ midPoint = interval_contains_open(self.get_view_interval(), self.get_loc() ) if midPoint: - if self.gridOn: self.gridline.draw(renderer) - if self.tick1On: self.tick1line.draw(renderer) - if self.tick2On: self.tick2line.draw(renderer) + if self.gridOn: + self.gridline.draw(renderer) + if self.tick1On: + self.tick1line.draw(renderer) + if self.tick2On: + self.tick2line.draw(renderer) if self.label1On: self.label1.draw(renderer) if self.label2On: self.label2.draw(renderer) @@ -213,8 +221,10 @@ 'return the view Interval instance for the axis tjis tick is ticking' raise NotImplementedError('Derived must override') + def set_view_interval(self, vmin, vmax, ignore=False): + raise NotImplementedError('Derived must override') - + class XTick(Tick): """ Contains all the Artists needed to make an x tick - the tick line, @@ -237,12 +247,7 @@ xaxis=True, ) - trans = blended_transform_factory( - self.axes.transData, self.axes.transAxes) - #offset the text downward with a post transformation - trans = trans + Affine2D().translate(0, -1 * self._padPixels) - t.set_transform(trans) - + t.set_transform(self.axes.get_xaxis_text1_transform(self._padPixels)) self._set_artist_props(t) return t @@ -262,11 +267,7 @@ horizontalalignment='center', ) - trans = blended_transform_factory( - self.axes.transData, self.axes.transAxes) - # offset the text upward with a post transformation - trans = trans + Affine2D().translate(0, self._padPixels) - t.set_transform( trans ) + t.set_transform(self.axes.get_xaxis_text2_transform(self._padPixels)) self._set_artist_props(t) return t @@ -280,8 +281,7 @@ marker = self._xtickmarkers[0], markersize=self._size, ) - l.set_transform(blended_transform_factory( - self.axes.transData, self.axes.transAxes) ) + l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) return l @@ -296,24 +296,20 @@ markersize=self._size, ) - l.set_transform(blended_transform_factory( - self.axes.transData, self.axes.transAxes) ) + l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) return l def _get_gridline(self, loc): 'Get the default line2D instance' # x in data coords, y in axes coords - l = Line2D( xdata=(loc, loc), ydata=(0, 1), - color=rcParams['grid.color'], - linestyle=rcParams['grid.linestyle'], - linewidth=rcParams['grid.linewidth'], - antialiased=False, - ) - l.set_transform( - blended_transform_factory( - self.axes.transData, self.axes.transAxes)) - l.set_clip_box(self.axes.bbox) + l = Line2D(xdata=(loc, loc), ydata=(0, 1.0), + 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) return l @@ -322,7 +318,6 @@ 'Set the location of tick in data coords with scalar loc' x = loc - self.tick1line.set_xdata((x,)) self.tick2line.set_xdata((x,)) self.gridline.set_xdata((x, )) @@ -334,6 +329,13 @@ 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervalx + def set_view_interval(self, vmin, vmax, ignore = False): + if ignore: + self.axes.viewLim.intervalx = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.axes.viewLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) + def get_minpos(self): return self.axes.dataLim.minposx @@ -363,12 +365,7 @@ dashdirection=0, xaxis=False, ) - trans = blended_transform_factory( - self.axes.transAxes, self.axes.transData) - # offset the text leftward with a post transformation - trans = trans + Affine2D().translate(-1 * self._padPixels, 0) - - t.set_transform( trans ) + t.set_transform(self.axes.get_yaxis_text1_transform(self._padPixels)) #t.set_transform( self.axes.transData ) self._set_artist_props(t) return t @@ -386,11 +383,7 @@ xaxis=False, horizontalalignment='left', ) - trans = blended_transform_factory( - self.axes.transAxes, self.axes.transData) - # offset the text rightward with a post transformation - trans = trans + Affine2D().translate(self._padPixels, 0) - t.set_transform( trans ) + t.set_transform(self.axes.get_yaxis_text2_transform(self._padPixels)) self._set_artist_props(t) return t @@ -404,9 +397,7 @@ linestyle = 'None', markersize=self._size, ) - l.set_transform( - blended_transform_factory( - self.axes.transAxes, self.axes.transData)) + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l @@ -420,9 +411,7 @@ markersize=self._size, ) - l.set_transform( - blended_transform_factory( - self.axes.transAxes, self.axes.transData)) + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l @@ -436,10 +425,7 @@ antialiased=False, ) - l.set_transform( blended_transform_factory( - self.axes.transAxes, self.axes.transData) ) - l.set_clip_box(self.axes.bbox) - + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l @@ -461,6 +447,13 @@ 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervaly + def set_view_interval(self, vmin, vmax): + if ignore: + self.axes.viewLim.intervaly = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.axes.viewLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) + def get_minpos(self): return self.axes.dataLim.minposy @@ -530,8 +523,8 @@ def get_children(self): children = [self.label] - majorticks = self.get_major_ticks() - minorticks = self.get_minor_ticks() + majorticks = self.get_major_ticks(len(self.major.locator())) + minorticks = self.get_minor_ticks(len(self.minor.locator())) children.extend(majorticks) children.extend(minorticks) @@ -565,11 +558,20 @@ self.units = None self.set_units(None) - + 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())) + for child in self.majorTicks + self.minorTicks: + child.set_clip_path(clippath, transform) + def get_view_interval(self): 'return the Interval instance for this axis view limits' raise NotImplementedError('Derived must override') + def set_view_interval(self, vmin, vmax, ignore=False): + raise NotImplementedError('Derived must override') + def get_data_interval(self): 'return the Interval instance for this axis data limits' raise NotImplementedError('Derived must override') @@ -585,8 +587,8 @@ ticklabelBoxes = [] ticklabelBoxes2 = [] - majorTicks = self.get_major_ticks() majorLocs = self.major.locator() + majorTicks = self.get_major_ticks(len(majorLocs)) self.major.formatter.set_locs(majorLocs) majorLabels = [self.major.formatter(val, i) for i, val in enumerate(majorLocs)] @@ -609,8 +611,8 @@ extent = tick.label2.get_window_extent(renderer) ticklabelBoxes2.append(extent) - minorTicks = self.get_minor_ticks() minorLocs = self.minor.locator() + minorTicks = self.get_minor_ticks(len(minorLocs)) self.minor.formatter.set_locs(minorLocs) minorLabels = [self.minor.formatter(val, i) for i, val in enumerate(minorLocs)] @@ -727,11 +729,9 @@ 'Get the formatter of the minor ticker' return self.minor.formatter - def get_major_ticks(self): + def get_major_ticks(self, numticks): 'get the tick instances; grow as necessary' - numticks = len(self.major.locator()) - if len(self.majorTicks)<numticks: # update the new tick label properties from the old protoTick = self.majorTicks[0] @@ -746,9 +746,8 @@ return ticks - def get_minor_ticks(self): + def get_minor_ticks(self, numticks): 'get the minor tick instances; grow as necessary' - numticks = len(self.minor.locator()) if len(self.minorTicks)<numticks: protoTick = self.minorTicks[0] for i in range(numticks-len(self.minorTicks)): @@ -949,8 +948,9 @@ ### XXX if the user changes units, the information will be lost here ticks = self.convert_units(ticks) self.set_major_locator( FixedLocator(ticks) ) - self.get_view_interval().update(ticks,0) - return self.get_major_ticks() + if len(ticks): + self.set_view_interval(min(ticks), max(ticks)) + return self.get_major_ticks(len(ticks)) def _update_label_position(self, bboxes, bboxes2): """ @@ -1153,6 +1153,13 @@ 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervalx + def set_view_interval(self, vmin, vmax, ignore=False): + if ignore: + self.axes.viewLim.intervalx = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.axes.viewLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) + def get_minpos(self): return self.axes.dataLim.minposx @@ -1357,6 +1364,13 @@ 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervaly + def set_view_interval(self, vmin, vmax, ignore=False): + if ignore: + self.axes.viewLim.intervaly = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.axes.viewLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) + def get_minpos(self): return self.axes.dataLim.minposy Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -537,7 +537,9 @@ """ Return the clip path """ - return self._clippath + if self._clippath is not None: + return self._clippath.get_transformed_path_and_affine() + return None, None def get_dashes(self): """ @@ -608,7 +610,7 @@ def set_clip_path(self, path): """ - Set the clip path + Set the clip path and transformation """ self._clippath = path @@ -758,7 +760,7 @@ return # Find all axes containing the mouse - axes_list = [a for a in self.canvas.figure.get_axes() if a.in_axes(x, y)] + axes_list = [a for a in self.canvas.figure.get_axes() if a.in_axes(self)] if len(axes_list) == 0: # None found self.inaxes = None @@ -1333,7 +1335,7 @@ if event.key!='a': n=int(event.key)-1 for i, a in enumerate(self.canvas.figure.get_axes()): - if event.x is not None and event.y is not None and a.in_axes(event.x, event.y): + if event.x is not None and event.y is not None and a.in_axes(event): if event.key=='a': a.set_navigate(True) else: @@ -1557,7 +1559,7 @@ 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(x, y) and a.get_navigate(): + 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())) self.canvas.mpl_disconnect(self._idDrag) self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan) @@ -1581,7 +1583,7 @@ 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(x, y) and a.get_navigate(): + 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())) self.press(event) @@ -1621,33 +1623,12 @@ def drag_pan(self, event): 'the drag callback in pan/zoom mode' - def format_deltas(event,dx,dy): - if event.key=='control': - if(abs(dx)>abs(dy)): - dy = dx - else: - dx = dy - elif event.key=='x': - dy = 0 - elif event.key=='y': - dx = 0 - elif event.key=='shift': - if 2*abs(dx) < abs(dy): - dx=0 - elif 2*abs(dy) < abs(dx): - dy=0 - elif(abs(dx)>abs(dy)): - dy=dy/abs(dy)*abs(dx) - else: - dx=dx/abs(dx)*abs(dy) - return (dx,dy) - for lastx, lasty, a, ind, old_lim, old_trans 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 - dx, dy = format_deltas(event, dx, dy) - a.drag_pan(self._button_pressed, lastx + dx, lasty + dy, lastx, lasty, old_lim, old_trans) + a.drag_pan(self._button_pressed, event.key, lastx, lasty, dx, dy, + old_lim, old_trans) self.dynamic_update() def release_zoom(self, event): @@ -1664,7 +1645,7 @@ self.draw() return - xmin, ymin, xmax, ymax = lim + xmin, ymin, xmax, ymax = lim.lbrt # zoom to rect inverse = a.transData.inverted() Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/cbook.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -1033,7 +1033,27 @@ itself. """ return self._mapping.get(a, [a]) - + + +def simple_linear_interpolation(a, steps): + steps = npy.floor(steps) + new_length = ((len(a) - 1) * steps) + 1 + new_shape = list(a.shape) + new_shape[0] = new_length + result = npy.zeros(new_shape, a.dtype) + + result[0] = a[0] + a0 = a[0:-1] + 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 + + return result + if __name__=='__main__': assert( allequal([1,1,1]) ) assert(not allequal([1,1,0]) ) Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/figure.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -7,7 +7,7 @@ import artist from artist import Artist -from axes import Axes, Subplot, PolarSubplot, PolarAxes +from axes import Axes, SubplotBase, subplot_class_factory from cbook import flatten, allequal, Stack, iterable, dedent import _image import colorbar as cbar @@ -22,6 +22,8 @@ from transforms import Affine2D, Bbox, BboxTransform, TransformedBbox from cm import ScalarMappable from contour import ContourSet +from projections import projection_factory, get_projection_names, \ + get_projection_class import warnings class SubplotParams: @@ -449,12 +451,17 @@ """ Add an a axes with axes rect [left, bottom, width, height] where all quantities are in fractions of figure width and height. kwargs are - legal Axes kwargs plus "polar" which sets whether to create a polar axes - + legal Axes kwargs plus "projection" which sets the projection type + of the axes. (For backward compatibility, polar=True may also be + provided, which is equivalent to projection='polar'). + Valid values for "projection" are: %s. Some of these projections + support additional kwargs, which may be provided to add_axes. + rect = l,b,w,h add_axes(rect) add_axes(rect, frameon=False, axisbg='g') add_axes(rect, polar=True) + add_axes(rect, projection='polar') add_axes(ax) # add an Axes instance @@ -470,10 +477,10 @@ add_axes(rect, label='axes2') The Axes instance will be returned - + The following kwargs are supported: - %(Axes)s - """ + %s + """ % (", ".join(get_projection_names()), '%(Axes)s') key = self._make_key(*args, **kwargs) @@ -489,13 +496,17 @@ else: rect = args[0] ispolar = kwargs.pop('polar', False) - + projection = kwargs.pop('projection', None) if ispolar: - a = PolarAxes(self, rect, **kwargs) - else: - a = Axes(self, rect, **kwargs) + if projection is not None and projection != 'polar': + raise ValueError( + "polar=True, yet projection='%s'. " + + "Only one of these arguments should be supplied." % + projection) + projection = 'polar' + + a = projection_factory(projection, self, rect, **kwargs) - self.axes.append(a) self._axstack.push(a) self.sca(a) @@ -513,15 +524,21 @@ add_subplot(111, polar=True) # add a polar subplot add_subplot(sub) # add Subplot instance sub - kwargs are legal Axes kwargs plus"polar" which sets whether to create a - polar axes. The Axes instance will be returned. + kwargs are legal Axes kwargs plus "projection", which chooses + a projection type for the axes. (For backward compatibility, + polar=True may also be provided, which is equivalent to + projection='polar'). Valid values for "projection" are: %s. + Some of these projections support additional kwargs, which may + be provided to add_axes. + The Axes instance will be returned. + If the figure already has a subplot with key *args, *kwargs then it will simply make that subplot current and return it The following kwargs are supported: - %(Axes)s - """ + %s + """ % (", ".join(get_projection_names()), "%(Axes)s") key = self._make_key(*args, **kwargs) if self._seen.has_key(key): @@ -532,16 +549,22 @@ if not len(args): return - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): + if isinstance(args[0], SubplotBase): a = args[0] assert(a.get_figure() is self) else: ispolar = kwargs.pop('polar', False) + projection = kwargs.pop('projection', None) if ispolar: - a = PolarSubplot(self, *args, **kwargs) - else: - a = Subplot(self, *args, **kwargs) + if projection is not None and projection != 'polar': + raise ValueError( + "polar=True, yet projection='%s'. " + + "Only one of these arguments should be supplied." % + projection) + projection = 'polar' + projection_class = get_projection_class(projection) + a = subplot_class_factory(projection_class)(self, *args, **kwargs) self.axes.append(a) self._axstack.push(a) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-10-02 08:30:29 UTC (rev 3907) +++ branches/transforms/lib/matplotlib/patches.py 2007-10-03 12:50:04 UTC (rev 3908) @@ -12,6 +12,8 @@ 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 @@ -72,6 +74,7 @@ self._linewidth = linewidth self._antialiased = antialiased self._hatch = hatch + self._combined_transform = transforms.IdentityTransform() self.fill = fill if len(kwargs): artist.setp(self, **kwargs) @@ -84,19 +87,15 @@ Returns T/F, {} """ - # MGDTODO: This will probably need to be implemented in C++ - + # This is a general version of contains should work on any + # patch with a path. However, patches that have a faster + # algebraic solution to hit-testing should override this + # method. if callable(self._contains): return self._contains(self,mouseevent) - try: - # TODO: make this consistent with patch collection algorithm - x, y = self.get_transform().inverse_xy_tup((mouseevent.x, mouseevent.y)) - xyverts = self.get_verts() - inside = nxutils.pnpoly(x, y, xyverts) - #print str(self),"%g,%g is in"%(x,y),xyverts,inside - return inside,{} - except ValueError: - return False,{} + inside = point_in_path(mouseevent.x, mouseevent.y, self.get_path(), + self.get_transform().frozen()) + return inside, {} def update_from(self, other): artist.Artist.update_from(self, other) @@ -109,6 +108,17 @@ self.set_figure(other.get_figure()) self.set_alpha(other.get_alpha()) + def get_transform(self): + return self._combined_transform + + def set_transform(self, t): + artist.Artist.set_transform(self, t) + self._combined_transform = self.get_patch_transform() + \ + artist.Artist.get_transform(self) + + def get_patch_transform(self): + return transforms.IdentityTransform() + def get_antialiased(self): return self._antialiased @@ -207,12 +217,12 @@ if not self.fill or self._facecolor is None: rgbFace = None else: rgbFace = colors.colorConverter.to_rgb(self._facecolor) - + if self._hatch: gc.set_hatch(self._hatch ) path = self.get_path() - transform = self.get_patch_transform() + self.get_transform() + transform = self.get_transform() renderer.draw_path(gc, path, transform, rgbFace) @@ -226,12 +236,10 @@ def get_window_extent(self, renderer=None): - verts = self.get_verts() - tverts = self.get_transform().seq_xy_tups(verts) - return transforms.bound_vertices(tverts) + trans_path = self.get_path().transformed(self.get_path_transform()) + return Bbox.unit().update_from_data(trans_path.vertices) - def set_lw(self, val): 'alias for set_linewidth' self.set_linewidth(val) @@ -350,6 +358,11 @@ def get_patch_transform(self): return self._rect_transform + + def contains(self, mouseevent): + x, y = self.get_transform().inverted().transform_point( + (mouseevent.x, mouseevent.y)) + return (x >= 0.0 and x <= 1.0 and y >= 0.0 and y <= 1.0), {} def get_x(self): "Return the left coord of the rectangle" @@ -445,8 +458,8 @@ def get_path(self): return self._path - def get_transform(self): - return self._poly_transform + self._transform + def get_patch_transform(self): + return self._poly_transform class Polygon(Patch): """ @@ -467,39 +480,35 @@ self._path = Path(xy, closed=True) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - def get_verts(self): + def get_path(self): return self._path - - # MGDTODO: Convert units - xs = self.convert_xunits(xs) - ys = self.convert_yunits(ys) - -class Wedge(Polygon): + +class Wedge(Patch): def __str__(self): return "Wedge(%g,%g)"%self.xy[0] - def __init__(self, center, r, theta1, theta2, - dtheta=0.1, **kwargs): + def __init__(self, center, r, theta1, theta2, **kwargs): """ Draw a wedge centered at x,y tuple center with radius r that sweeps theta1 to theta2 (angles) - dtheta is the resolution in degrees - Valid kwargs are: %(Patch)s """ - # MGDTODO: Implement me - xc, yc = center - rads = (math.pi/180.)*npy.arange(theta1, theta2+0.1*dtheta, dtheta) - xs = r*npy.cos(rads)+xc - ys = r*npy.sin(rads)+yc - ... [truncated message content] |