From: <md...@us...> - 2007-10-29 15:21:55
|
Revision: 4053 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4053&view=rev Author: mdboom Date: 2007-10-29 08:21:49 -0700 (Mon, 29 Oct 2007) Log Message: ----------- Lots of minor bug fixes. Modified Paths: -------------- branches/transforms/PASSED_DEMOS 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_svg.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/colors.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/widgets.py Modified: branches/transforms/PASSED_DEMOS =================================================================== --- branches/transforms/PASSED_DEMOS 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/PASSED_DEMOS 2007-10-29 15:21:49 UTC (rev 4053) @@ -208,3 +208,5 @@ xcorr_demo.py O zoom_window.py O zorder_demo.py O + +MGDTODO: units directory \ No newline at end of file Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/artist.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -2,6 +2,7 @@ import re, warnings from cbook import iterable, flatten from transforms import Bbox, IdentityTransform, TransformedBbox, TransformedPath +from path import Path ## Note, matplotlib artists use the doc strings for set and get # methods to enable the introspection methods of setp and getp. Every @@ -285,25 +286,48 @@ def set_clip_path(self, path, transform=None): """ - Set the artist's clip path + Set the artist's clip path, which may be: - ACCEPTS: a Path instance and a Transform instance, or a Patch instance + a) a Patch (or subclass) instance + + b) a Path instance, in which cas aoptional transform may + be provided, which will be applied to the path before using it + for clipping. + + c) None, to remove the clipping path + + For efficiency, if the path happens to be an axis-aligned + rectangle, this method will set the clipping box to the + corresponding rectangle and set the clipping path to None. + + ACCEPTS: a Path instance and a Transform instance, a Patch + instance, or None """ from patches import Patch, Rectangle + + success = False if transform is None: if isinstance(path, Rectangle): self.clipbox = TransformedBbox(Bbox.unit(), path.get_transform()) + success = True 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: + success = True + + if path is None: + self._clippath = None + success = True + elif isinstance(path, Path): self._clippath = TransformedPath(path, transform) - self._clipon = self.clipbox is not None or path is not None + success = True + + if not success: + print type(path), type(transform) + raise TypeError("Invalid arguments to set_clip_path") + + self._clipon = self.clipbox is not None or self._clippath is not None self.pchanged() def get_alpha(self): @@ -334,6 +358,10 @@ return self._clippath def get_transformed_clip_path_and_affine(self): + ''' + Return the clip path with the non-affine part of its transformation applied, + and the remaining affine part of its transformation. + ''' if self._clippath is not None: return self._clippath.get_transformed_path_and_affine() return None, None Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -494,9 +494,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() @@ -553,7 +550,8 @@ def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the - transData and transAxes Transformation attributes + transScale, transData, transLimits and transAxes + transformations. """ self.dataLim = mtransforms.Bbox.unit() self.viewLim = mtransforms.Bbox.unit() @@ -579,27 +577,105 @@ self.axes.transAxes, self.axes.transData) def get_xaxis_transform(self): + """ + Get the transformation used for drawing x-axis labels, ticks + and gridlines. The x-direction is in data coordinates and the + y-direction is in axis coordinates. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return self._xaxis_transform def get_xaxis_text1_transform(self, pad_pixels): + """ + Get the transformation used for drawing x-axis labels, which + will add the given number of pad_pixels between the axes and + the label. The x-direction is in data coordinates and the + y-direction is in axis coordinates. Returns a 3-tuple of the + form: + + (transform, valign, halign) + + where valign and halign are requested alignments for the text. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return (self._xaxis_transform + mtransforms.Affine2D().translate(0, -1 * pad_pixels), "top", "center") def get_xaxis_text2_transform(self, pad_pixels): + """ + Get the transformation used for drawing the secondary x-axis + labels, which will add the given number of pad_pixels between + the axes and the label. The x-direction is in data + coordinates and the y-direction is in axis coordinates. + Returns a 3-tuple of the form: + + (transform, valign, halign) + + where valign and halign are requested alignments for the text. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return (self._xaxis_transform + mtransforms.Affine2D().translate(0, pad_pixels), "bottom", "center") def get_yaxis_transform(self): + """ + Get the transformation used for drawing y-axis labels, ticks + and gridlines. The x-direction is in axis coordinates and the + y-direction is in data coordinates. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return self._yaxis_transform def get_yaxis_text1_transform(self, pad_pixels): + """ + Get the transformation used for drawing y-axis labels, which + will add the given number of pad_pixels between the axes and + the label. The x-direction is in axis coordinates and the + y-direction is in data coordinates. Returns a 3-tuple of the + form: + + (transform, valign, halign) + + where valign and halign are requested alignments for the text. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return (self._yaxis_transform + mtransforms.Affine2D().translate(-1 * pad_pixels, 0), "center", "right") def get_yaxis_text2_transform(self, pad_pixels): + """ + Get the transformation used for drawing the secondary y-axis + labels, which will add the given number of pad_pixels between + the axes and the label. The x-direction is in axis + coordinates and the y-direction is in data coordinates. + Returns a 3-tuple of the form: + + (transform, valign, halign) + + where valign and halign are requested alignments for the text. + + This transformation is primarily used by the Axis class, and + is meant to be overridden by new kinds of projections that may + need to place axis elements in different locations. + """ return (self._yaxis_transform + mtransforms.Affine2D().translate(pad_pixels, 0), "center", "left") @@ -610,11 +686,11 @@ self.xaxis.get_transform(), self.yaxis.get_transform())) def get_position(self, original=False): - 'Return the axes rectangle left, bottom, width, height' + 'Return the a copy of the axes rectangle as a Bbox' if original: - return self._originalPosition + return self._originalPosition.frozen() else: - return self._position + return self._position.frozen() def set_position(self, pos, which='both'): @@ -639,7 +715,6 @@ 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) @@ -648,6 +723,16 @@ a.axes = self def get_axes_patch(self): + """ + Returns the patch used to draw the background of the axes. It + is also used as the clipping path for any data elements on the + axes. + + In the standard axes, this is a rectangle, but in other + projections it may not be. + + Intended to be overridden by new projection types. + """ return mpatches.Rectangle((0.0, 0.0), 1.0, 1.0) def cla(self): @@ -806,6 +891,12 @@ ', '.join(mtransforms.BBox.coefs.keys())) def get_data_ratio(self): + """ + Returns the aspect ratio of the raw data. + + This method is intended to be overridden by new projection + types. + """ xmin,xmax = self.get_xbound() xsize = max(math.fabs(xmax-xmin), 1e-30) ymin,ymax = self.get_ybound() @@ -1032,6 +1123,7 @@ a.set_axes(self) self.artists.append(a) self._set_artist_props(a) + # MGDTODO: We may not want to do this -- the old trunk didn't a.set_clip_path(self.axesPatch) a._remove_method = lambda h: self.artists.remove(h) @@ -1074,19 +1166,20 @@ self._update_patch_limits(p) self.patches.append(p) p._remove_method = lambda h: self.patches.remove(h) - + def _update_patch_limits(self, p): 'update the datalimits for patch p' - xys = self._get_verts_in_data_coords( - p.get_data_transform(), - p.get_patch_transform().transform(p.get_path().vertices)) - self.update_datalim(xys) + vertices = p.get_patch_transform().transform(p.get_path().vertices) + if p.get_data_transform() != self.transData: + transform = p.get_data_transform() + self.transData.inverted() + xys = transform.transform(vertices) + self.update_datalim(vertices) - def add_table(self, tab): 'Add a table instance to the list of axes tables' self._set_artist_props(tab) self.tables.append(tab) + # MGDTODO: We may not want to do this (the old version in trunk didn't) tab.set_clip_path(self.axesPatch) tab._remove_method = lambda h: self.tables.remove(h) @@ -1105,8 +1198,6 @@ # 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]) @@ -1117,7 +1208,6 @@ # 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 - ## self.dataLim.update_numerix(x, y, -1) self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits) self.ignore_existing_data_limits = False @@ -1125,16 +1215,6 @@ 'Update the datalim to include the given Bbox' self.dataLim.set(Bbox.union([self.dataLim, bounds])) - def _get_verts_in_data_coords(self, trans, xys): - if trans == self.transData: - return xys - # data is not in axis data units. We must transform it to - # 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.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' @@ -1162,7 +1242,7 @@ self.yaxis.set_units(yunits) def in_axes(self, mouseevent): - 'return True is the point xwin, ywin (display coords) are in the Axes' + 'return True if the given mouseevent (in display coords) is in the Axes' return self.axesPatch.contains(mouseevent)[0] def get_autoscale_on(self): @@ -1268,11 +1348,11 @@ if self.axison and self._frameon: artists.append(self.axesFrame) - dsu = [ (a.zorder, a) for a in artists + dsu = [ (a.zorder, i, a) for i, a in enumerate(artists) if not a.get_animated() ] dsu.sort() - for zorder, a in dsu: + for zorder, i, a in dsu: a.draw(renderer) renderer.close_group('axes') @@ -1474,16 +1554,10 @@ self.set_xlim(upper, lower) def get_xlim(self): - """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 + Get the x-axis range [xmin, xmax] + """ + return self.viewLim.intervalx def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs): """ @@ -1637,16 +1711,10 @@ self.set_ylim(upper, lower) def get_ylim(self): - """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 + Get the y-axis range [xmin, xmax] + """ + return self.viewLim.intervaly def set_ylim(self, ymin=None, ymax=None, emit=True, **kwargs): """ @@ -1812,8 +1880,10 @@ def format_coord(self, x, y): 'return a format string formatting the x, y coord' - if x is None or y is None: - return '' + if x is None: + x = '???' + if y is None: + y = '???' xs = self.format_xdata(x) ys = self.format_ydata(y) return 'x=%s, y=%s'%(xs,ys) @@ -1855,6 +1925,17 @@ self._navigate_mode = b def start_pan(self, x, y, button): + """ + Called when a pan operation has started. + + x, y are the mouse coordinates in display coords. + button is the mouse button number: + 1: LEFT + 2: MIDDLE + 3: RIGHT + + Intended to be overridden by new projection types. + """ self._pan_start = cbook.Bunch( lim = self.viewLim.frozen(), trans = self.transData.frozen(), @@ -1864,9 +1945,29 @@ ) def end_pan(self): + """ + Called when a pan operation completes (when the mouse button + is up.) + + Intended to be overridden by new projection types. + """ del self._pan_start def drag_pan(self, button, key, x, y): + """ + Called when the mouse moves during a pan operation. + + button is the mouse button number: + 1: LEFT + 2: MIDDLE + 3: RIGHT + + key is a "shift" key + + x, y are the mouse coordinates in display coords. + + Intended to be overridden by new projection types. + """ def format_deltas(key, dx, dy): if key=='control': if(abs(dx)>abs(dy)): @@ -2223,7 +2324,7 @@ %(Annotation)s """ a = mtext.Annotation(*args, **kwargs) - a.set_transform(mtransforms.Affine2D()) + a.set_transform(mtransforms.IdentityTransform()) self._set_artist_props(a) if kwargs.has_key('clip_on'): a.set_clip_path(self.axesPatch) self.texts.append(a) @@ -4035,9 +4136,9 @@ if len(sh) == 1 and sh[0] == len(x): colors = None # use cmap, norm after collection is created else: - colors = mcolors.colorConverter.to_rgba_list(c, alpha) + colors = mcolors.colorConverter.to_rgba_array(c, alpha) else: - colors = mcolors.colorConverter.to_rgba_list(c, alpha) + colors = mcolors.colorConverter.to_rgba_array(c, alpha) if not iterable(s): scales = (s,) @@ -4137,7 +4238,7 @@ offsets = zip(x,y), transOffset = self.transData, ) - collection.set_transform(mtransforms.Affine2D()) + collection.set_transform(mtransforms.IdentityTransform()) collection.set_alpha(alpha) collection.update(kwargs) @@ -4198,6 +4299,9 @@ quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc def quiver(self, *args, **kw): + """ + MGDTODO: Document me + """ q = mquiver.Quiver(self, *args, **kw) self.add_collection(q, False) self.update_datalim_numerix(q.X, q.Y) Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -14,7 +14,7 @@ from font_manager import FontProperties from text import Text, TextWithDash from transforms import Affine2D, Bbox, blended_transform_factory, \ - interval_contains + IdentityTransform, interval_contains from patches import bbox_artist from scale import scale_factory @@ -113,6 +113,7 @@ self.tick1line.set_clip_path(clippath, transform) self.tick2line.set_clip_path(clippath, transform) self.gridline.set_clip_path(clippath, transform) + set_clip_path.__doc__ = Artist.set_clip_path.__doc__ def contains(self, mouseevent): """Test whether the mouse event occured in the Tick marks. @@ -1015,7 +1016,7 @@ horizontalalignment='center', ) label.set_transform( blended_transform_factory( - self.axes.transAxes, Affine2D() )) + self.axes.transAxes, IdentityTransform() )) self._set_artist_props(label) self.label_position='bottom' @@ -1030,7 +1031,7 @@ horizontalalignment='right', ) offsetText.set_transform( blended_transform_factory( - self.axes.transAxes, Affine2D() )) + self.axes.transAxes, IdentityTransform() )) self._set_artist_props(offsetText) self.offset_text_position='bottom' return offsetText @@ -1092,11 +1093,11 @@ def set_ticks_position(self, position): """ - Set the ticks position (top, bottom, both or default) - both sets the ticks to appear on both positions, but - does not change the tick labels. - default resets the tick positions to the default: - ticks on both positions, labels at bottom. + Set the ticks position (top, bottom, both, default or none) + both sets the ticks to appear on both positions, but does not + change the tick labels. default resets the tick positions to + the default: ticks on both positions, labels at bottom. none + can be used if you don't want any ticks. ACCEPTS: [ 'top' | 'bottom' | 'both' | 'default' | 'none' ] """ @@ -1225,7 +1226,7 @@ rotation='vertical', ) label.set_transform( blended_transform_factory( - Affine2D(), self.axes.transAxes) ) + IdentityTransform(), self.axes.transAxes) ) self._set_artist_props(label) self.label_position='left' @@ -1240,7 +1241,7 @@ horizontalalignment = 'left', ) offsetText.set_transform(blended_transform_factory( - self.axes.transAxes, Affine2D()) ) + self.axes.transAxes, IdentityTransform()) ) self._set_artist_props(offsetText) self.offset_text_position='left' return offsetText Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -73,7 +73,7 @@ offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds): path, transform = path_id - transform = transform.frozen().translate(xo, yo) + transform = transforms.Affine2D(transform.get_matrix()).translate(xo, yo) self.draw_path(gc, path, transform, rgbFace) def _iter_collection_raw_paths(self, master_transform, paths, all_transforms): @@ -200,6 +200,27 @@ """ return False + 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 + + **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 + + if 0: bbox_artist(self, renderer) + + 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 Is used for drawing text (text.py) and images (image.py) only @@ -386,7 +407,8 @@ def set_clip_path(self, path): """ - Set the clip path and transformation + Set the clip path and transformation. Path should be a + transforms.TransformedPath instance. """ assert path is None or isinstance(path, transforms.TransformedPath) self._clippath = path Modified: branches/transforms/lib/matplotlib/backends/backend_svg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -205,7 +205,7 @@ for i, (path, transform) in enumerate(self._iter_collection_raw_paths( master_transform, paths, all_transforms)): name = 'coll%x_%x' % (self._path_collection_id, i) - transform = transform.frozen().scale(1.0, -1.0) + transform = Affine2D(transform.get_matrix()).scale(1.0, -1.0) d = self._convert_path(path, transform) write('<path id="%s" d="%s"/>\n' % (name, d)) path_codes.append(name) Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -83,12 +83,12 @@ if antialiaseds is None: antialiaseds = (mpl.rcParams['patch.antialiased'],) self.set_linestyles(linestyles) - self._facecolors = _colors.colorConverter.to_rgba_list(facecolors) + self._facecolors = _colors.colorConverter.to_rgba_array(facecolors) if edgecolors == 'None': self._edgecolors = self._facecolors linewidths = (0,) else: - self._edgecolors = _colors.colorConverter.to_rgba_list(edgecolors) + self._edgecolors = _colors.colorConverter.to_rgba_array(edgecolors) self._linewidths = self._get_value(linewidths) self._antialiaseds = self._get_value(antialiaseds) @@ -268,7 +268,7 @@ ACCEPTS: matplotlib color arg or sequence of rgba tuples """ - self._facecolors = _colors.colorConverter.to_rgba_list(c, self._alpha) + self._facecolors = _colors.colorConverter.to_rgba_array(c, self._alpha) set_facecolors = set_facecolor @@ -284,7 +284,7 @@ self._linewidths = (0.0,) self._edgecolors = npy.array([]) else: - self._edgecolors = _colors.colorConverter.to_rgba_list(c) + self._edgecolors = _colors.colorConverter.to_rgba_array(c) set_edgecolors = set_edgecolor def set_alpha(self, alpha): @@ -581,7 +581,7 @@ if antialiaseds is None: antialiaseds = (mpl.rcParams['lines.antialiased'],) self.set_linestyles(linestyles) - colors = _colors.colorConverter.to_rgba_list(colors) + colors = _colors.colorConverter.to_rgba_array(colors) Collection.__init__( self, @@ -633,7 +633,7 @@ ACCEPTS: matplotlib color arg or sequence of rgba tuples """ - self._edgecolors = _colors.colorConverter.to_rgba_list(c) + self._edgecolors = _colors.colorConverter.to_rgba_array(c) def color(self, c): """ Modified: branches/transforms/lib/matplotlib/colors.py =================================================================== --- branches/transforms/lib/matplotlib/colors.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/colors.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -307,12 +307,13 @@ except (TypeError, ValueError), exc: raise ValueError('to_rgba: Invalid rgba arg "%s"\n%s' % (str(arg), exc)) - def to_rgba_list(self, c, alpha=None): + def to_rgba_array(self, c, alpha=None): """ - Returns a list of rgba tuples. + Returns an Numpy array of rgba tuples. Accepts a single mpl color spec or a sequence of specs. - If the sequence is a list, the list items are changed in place. + If the sequence is a list or array, the items are changed in place, + but an array copy is still returned. """ try: result = [self.to_rgba(c, alpha)] @@ -320,7 +321,7 @@ # If c is a list it must be maintained as the same list # with modified items so that items can be appended to # it. This is needed for examples/dynamic_collections.py. - if not isinstance(c, list): # specific; don't need duck-typing + if not isinstance(c, (list, npy.ndarray)): # specific; don't need duck-typing c = list(c) for i, cc in enumerate(c): c[i] = self.to_rgba(cc, alpha) # change in place Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/legend.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -363,15 +363,6 @@ bbox.update_from_data_xy(averts, True) bboxes.append(bbox) - for handle in ax.collections: - if isinstance(handle, LineCollection): - hlines = handle.get_lines() - trans = handle.get_transform() - for line in hlines: - tline = trans.seq_xy_tups(line) - aline = [inv(v) for v in tline] - lines.append(aline) - return [vertices, bboxes, lines] def draw_frame(self, b): Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -71,6 +71,45 @@ ic1 = breakpoints return npy.concatenate((ic0[:, npy.newaxis], ic1[:, npy.newaxis]), axis=1) +def segment_hits(cx,cy,x,y,radius): + """Determine if any line segments are within radius of a point. Returns + the list of line segments that are within that radius. + """ + # Process single points specially + if len(x) < 2: + res, = npy.nonzero( (cx - x)**2 + (cy - y)**2 <= radius**2 ) + return res + + # We need to lop the last element off a lot. + xr,yr = x[:-1],y[:-1] + + # Only look at line segments whose nearest point to C on the line + # lies within the segment. + dx,dy = x[1:]-xr, y[1:]-yr + Lnorm_sq = dx**2+dy**2 # Possibly want to eliminate Lnorm==0 + u = ( (cx-xr)*dx + (cy-yr)*dy )/Lnorm_sq + candidates = (u>=0) & (u<=1) + #if any(candidates): print "candidates",xr[candidates] + + # Note that there is a little area near one side of each point + # which will be near neither segment, and another which will + # be near both, depending on the angle of the lines. The + # following radius test eliminates these ambiguities. + point_hits = (cx - x)**2 + (cy - y)**2 <= radius**2 + #if any(point_hits): print "points",xr[candidates] + candidates = candidates & ~point_hits[:-1] & ~point_hits[1:] + + # For those candidates which remain, determine how far they lie away + # from the line. + px,py = xr+u*dx,yr+u*dy + line_hits = (cx-px)**2 + (cy-py)**2 <= radius**2 + #if any(line_hits): print "lines",xr[candidates] + line_hits = line_hits & candidates + points, = point_hits.ravel().nonzero() + lines, = line_hits.ravel().nonzero() + #print points,lines + return npy.concatenate((points,lines)) + class Line2D(Artist): lineStyles = _lineStyles = { # hidden names deprecated '-' : '_draw_solid', @@ -276,7 +315,6 @@ else: 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) @@ -320,7 +358,7 @@ # correct for marker size, if any if self._marker is not None: ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5 - bbox = Bbox(bbox.get_points() + [[-ms, -ms], [ms, ms]]) + bbox = bbox.padded(ms) return bbox def set_axes(self, ax): @@ -399,7 +437,7 @@ """ set the Transformation instance used by this artist - ACCEPTS: a matplotlib.transform transformation instance + ACCEPTS: a matplotlib.transforms.Transform instance """ Artist.set_transform(self, t) self._transformed_path = TransformedPath(self._path, self.get_transform()) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/patches.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -82,7 +82,7 @@ Returns T/F, {} """ - # This is a general version of contains should work on any + # This is a general version of contains that should work on any # patch with a path. However, patches that have a faster # algebraic solution to hit-testing should override this # method. @@ -290,8 +290,8 @@ Patch.__init__(self) self.patch = patch self.props = props - self.ox, self.oy = ox, oy - self._shadow_transform = transforms.Affine2D().translate(self.ox, self.oy) + self._ox, self._oy = ox, oy + self._update_transform() self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -308,7 +308,22 @@ self.set_facecolor((r,g,b,0.5)) self.set_edgecolor((r,g,b,0.5)) - + + def _update_transform(self): + self._shadow_transform = transforms.Affine2D().translate(self._ox, self._oy) + + def _get_ox(self): + return self._ox + def _set_ox(self, ox): + self._ox = ox + self._update_transform() + + def _get_oy(self): + return self._oy + def _set_oy(self, oy): + self._oy = oy + self._update_transform() + def get_path(self): return self.patch.get_path() Modified: branches/transforms/lib/matplotlib/widgets.py =================================================================== --- branches/transforms/lib/matplotlib/widgets.py 2007-10-29 15:20:13 UTC (rev 4052) +++ branches/transforms/lib/matplotlib/widgets.py 2007-10-29 15:21:49 UTC (rev 4053) @@ -849,7 +849,7 @@ self.prev = (0, 0) if self.direction == 'horizontal': - trans = (self.ax.transData, self.ax.transAxes) + trans = blended_transform_factory(self.ax.transData, self.ax.transAxes) w,h = 0,1 else: trans = blended_transform_factory(self.ax.transAxes, self.ax.transData) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |