From: <md...@us...> - 2007-10-15 13:49:52
|
Revision: 3947 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3947&view=rev Author: mdboom Date: 2007-10-15 06:49:25 -0700 (Mon, 15 Oct 2007) Log Message: ----------- Significant speed improvement in text layout. Reverted to fix bug in ticklabels. Lots of other minor things. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -1037,6 +1037,7 @@ a.set_axes(self) self.artists.append(a) self._set_artist_props(a) + a.set_clip_path(self.axesPatch) a._remove_method = lambda h: self.artists.remove(h) def add_collection(self, collection, autolim=False): @@ -1091,6 +1092,7 @@ 'Add a table instance to the list of axes tables' self._set_artist_props(tab) self.tables.append(tab) + tab.set_clip_path(self.axesPatch) tab._remove_method = lambda h: self.tables.remove(h) def relim(self): Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -91,7 +91,6 @@ self._size = size self._padPixels = self.figure.dpi * self._pad * (1/72.0) - self._locTransform = Affine2D() self.tick1line = self._get_tick1line() self.tick2line = self._get_tick2line() @@ -286,7 +285,7 @@ markersize=self._size, ) - l.set_transform(self._locTransform + self.axes.get_xaxis_transform()) + l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) return l @@ -298,16 +297,20 @@ linestyle=rcParams['grid.linestyle'], linewidth=rcParams['grid.linewidth'], ) - l.set_transform(self._locTransform + self.axes.get_xaxis_transform()) + l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) return l def update_position(self, loc): 'Set the location of tick in data coords with scalar loc' - self._locTransform.clear().translate(loc, 0.0) - self.label1.set_x(loc) - self.label2.set_x(loc) + x = loc + + self.tick1line.set_xdata((x,)) + self.tick2line.set_xdata((x,)) + self.gridline.set_xdata((x, )) + self.label1.set_x(x) + self.label2.set_x(x) self._loc = loc def get_view_interval(self): @@ -385,7 +388,7 @@ linestyle = 'None', markersize=self._size, ) - l.set_transform(self._locTransform + self.axes.get_yaxis_transform()) + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l @@ -398,7 +401,7 @@ markersize=self._size, ) - l.set_transform(self._locTransform + self.axes.get_yaxis_transform()) + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l @@ -411,19 +414,24 @@ linewidth=rcParams['grid.linewidth'], ) - l.set_transform(self._locTransform + self.axes.get_yaxis_transform()) + l.set_transform(self.axes.get_yaxis_transform()) self._set_artist_props(l) return l def update_position(self, loc): 'Set the location of tick in data coords with scalar loc' - self._locTransform.clear().translate(0.0, loc) - self.label1.set_y(loc) - self.label2.set_y(loc) + y = loc + self.tick1line.set_ydata((y,)) + self.tick2line.set_ydata((y,)) + self.gridline.set_ydata((y, )) + + self.label1.set_y( y ) + self.label2.set_y( y ) + self._loc = loc - + def get_view_interval(self): 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervaly @@ -751,7 +759,7 @@ if len(self.minorTicks) < numticks: # update the new tick label properties from the old for i in range(numticks - len(self.minorTicks)): - tick = self._get_tick(minor=True) + tick = self._get_tick(major=False) self.minorTicks.append(tick) if self._lastNumMinorTicks < numticks: Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -43,7 +43,7 @@ linewidths=None, antialiaseds = None, offsets = None, - transOffset = transforms.identity_transform(), + transOffset = transforms.IdentityTransform(), norm = None, # optional for cm.ScalarMappable cmap = None, # ditto @@ -376,7 +376,7 @@ linewidths=None, antialiaseds = None, offsets = None, - transOffset = transforms.identity_transform(), + transOffset = transforms.IdentityTransform(), norm = None, # optional for cm.ScalarMappable cmap = None, # ditto Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -455,8 +455,10 @@ gc.set_capstyle(cap) funcname = self._lineStyles.get(self._linestyle, '_draw_nothing') - lineFunc = getattr(self, funcname) - lineFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) + if funcname != '_draw_nothing': + tpath, affine = self._transformed_path.get_transformed_path_and_affine() + lineFunc = getattr(self, funcname) + lineFunc(renderer, gc, tpath, affine) if self._marker is not None: gc = renderer.new_gc() @@ -465,8 +467,10 @@ gc.set_linewidth(self._markeredgewidth) gc.set_alpha(self._alpha) funcname = self._markers.get(self._marker, '_draw_nothing') - markerFunc = getattr(self, funcname) - markerFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) + if funcname != '_draw_nothing': + tpath, affine = self._transformed_path.get_transformed_path_and_affine() + markerFunc = getattr(self, funcname) + markerFunc(renderer, gc, tpath, affine) renderer.close_group('line2d') @@ -621,11 +625,9 @@ ACCEPTS: npy.array """ - try: del self._xsorted - except AttributeError: pass + self._xorig = x + self.recache() - self.set_data(x, self.get_ydata()) - def set_ydata(self, y): """ Set the data npy.array for y @@ -633,9 +635,9 @@ ACCEPTS: npy.array """ - self.set_data(self.get_xdata(), y) + self._yorig = y + self.recache() - def set_dashes(self, seq): """ Set the dash sequence, sequence of dashes with on off ink in Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/path.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -84,7 +84,8 @@ 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 not ma.isMaskedArray(vertices): + vertices = ma.asarray(vertices, npy.float_) if codes is None: if len(vertices) == 0: Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/text.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -172,24 +172,18 @@ self._linespacing = other._linespacing def _get_layout(self, renderer): - # layout the xylocs in display coords as if angle = zero and - # then rotate them around self._x, self._y - #return _unit_box key = self.get_prop_tup() if self.cached.has_key(key): return self.cached[key] + horizLayout = [] - 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 + thisx, thisy = 0.0, 0.0 + xmin, ymin = 0.0, 0.0 + width, height = 0.0, 0.0 lines = self._text.split('\n') - whs = [] + whs = npy.zeros((len(lines), 2)) + horizLayout = npy.zeros((len(lines), 4)) # Find full vertical extent of font, # including ascenders and descenders: tmp, heightt, bl = renderer.get_text_width_height_descent( @@ -197,43 +191,39 @@ offsety = heightt * self._linespacing baseline = None - for line in lines: + for i, line in enumerate(lines): w, h, d = renderer.get_text_width_height_descent( line, self._fontproperties, ismath=self.is_math_text(line)) if baseline is None: baseline = h - d - whs.append( (w,h) ) - horizLayout.append((line, thisx, thisy, w, h)) + whs[i] = w, h + horizLayout[i] = thisx, thisy, w, h thisy -= offsety width = max(width, w) - ymin = horizLayout[-1][2] - ymax = horizLayout[0][2] + horizLayout[0][-1] + ymin = horizLayout[-1][1] + ymax = horizLayout[0][1] + horizLayout[0][3] height = ymax-ymin - xmax = xmin + width + # get the rotation matrix - M = self.get_rotation_matrix(xmin, ymin) + M = Affine2D().rotate_deg(self.get_rotation()) - # the corners of the unrotated bounding box - cornersHoriz = npy.array( - [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)], - npy.float_) offsetLayout = npy.zeros((len(lines), 2)) + offsetLayout[:] = horizLayout[:, 0:2] # now offset the individual text lines within the box if len(lines)>1: # do the multiline aligment malign = self._get_multialignment() - for i, (line, thisx, thisy, w, h) in enumerate(horizLayout): - if malign=='center': offsetx = width/2.0-w/2.0 - elif malign=='right': offsetx = width-w - else: offsetx = 0 - thisx += offsetx - offsetLayout[i] = (thisx, thisy) - else: # no additional layout needed - offsetLayout[0] = horizLayout[0][1:3] + if malign == 'center': + offsetLayout[:, 0] += width/2.0 - horizLayout[:, 2] / 2.0 + elif malign == 'right': + offsetLayout[:, 0] += width - horizLayout[:, 2] + # the corners of the unrotated bounding box + cornersHoriz = npy.array( + [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)], + npy.float_) # now rotate the bbox - cornersRotated = M.transform(cornersHoriz) txs = cornersRotated[:, 0] @@ -251,37 +241,30 @@ # compute the text location in display coords and the offsets # necessary to align the bbox with that location - tx, ty = self._get_xy_display() + if halign=='center': offsetx = (xmin + width/2.0) + elif halign=='right': offsetx = (xmin + width) + else: offsetx = xmin - if halign=='center': offsetx = tx - (xmin + width/2.0) - elif halign=='right': offsetx = tx - (xmin + width) - else: offsetx = tx - xmin + if valign=='center': offsety = (ymin + height/2.0) + elif valign=='top': offsety = (ymin + height) + elif valign=='baseline': offsety = (ymin + height) + baseline + else: offsety = ymin - if valign=='center': offsety = ty - (ymin + height/2.0) - elif valign=='top': offsety = ty - (ymin + height) - elif valign=='baseline': offsety = ty - (ymin + height) + baseline - else: offsety = ty - ymin + xmin -= offsetx + ymin -= offsety - xmin += offsetx - ymin += offsety - bbox = Bbox.from_lbwh(xmin, ymin, width, height) # now rotate the positions around the first x,y position xys = M.transform(offsetLayout) - xys += (offsetx, offsety) + xys -= (offsetx, offsety) - # now inverse transform back to data coords - inverse_transform = transform.inverted() - xys = inverse_transform.transform(xys) - xs, ys = xys[:, 0], xys[:, 1] ret = bbox, zip(lines, whs, xs, ys) self.cached[key] = ret return ret - def set_bbox(self, rectprops): """ Draw a bounding box around self. rect props are any settable @@ -307,18 +290,20 @@ if self.get_clip_on(): gc.set_clip_rectangle(self.clipbox) - - if self._bbox: bbox_artist(self, renderer, self._bbox) angle = self.get_rotation() bbox, info = self._get_layout(renderer) trans = self.get_transform() + posx, posy = self.get_position() + posx, posy = trans.transform_point((posx, posy)) + canvasw, canvash = renderer.get_canvas_width_height() + if rcParams['text.usetex']: - canvasw, canvash = renderer.get_canvas_width_height() for line, wh, x, y in info: - x, y = trans.transform_point((x, y)) + x = x + posx + y = y + posy if renderer.flipy(): y = canvash-y @@ -326,9 +311,9 @@ self._fontproperties, angle) return - canvasw, canvash = renderer.get_canvas_width_height() for line, wh, x, y in info: - x, y = trans.transform_point((x, y)) + x = x + posx + y = y + posy if renderer.flipy(): y = canvash-y @@ -402,8 +387,7 @@ x, y = self.get_position() return (x, y, self._text, self._color, self._verticalalignment, self._horizontalalignment, - hash(self._fontproperties), self._rotation, - self.get_transform().id + hash(self._fontproperties), self._rotation ) def get_text(self): @@ -432,11 +416,11 @@ angle = self.get_rotation() bbox, info = self._get_layout(self._renderer) + x, y = self.get_position() + x, y = self.get_transform().transform_point((x, y)) + bbox = bbox.translated(x, y) return bbox - def get_rotation_matrix(self, x0, y0): - return Affine2D().rotate_deg_around(x0, y0, self.get_rotation()) - def set_backgroundcolor(self, color): """ Set the background color of the text by updating the bbox (see set_bbox for more info) Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-14 21:14:18 UTC (rev 3946) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-15 13:49:25 UTC (rev 3947) @@ -34,8 +34,6 @@ DEBUG = False -# MGDTODO: Cache get_affine??? - class TransformNode(object): """ TransformNode is the base class for anything that participates in @@ -51,7 +49,7 @@ # INVALID_AFFINE_ONLY INVALID_NON_AFFINE = 1 INVALID_AFFINE = 2 - INVALID = INVALID_NON_AFFINE & INVALID_AFFINE + INVALID = INVALID_NON_AFFINE | INVALID_AFFINE # Some metadata about the transform, used to determine whether an # invalidation is affine-only @@ -71,11 +69,6 @@ # them alive. self._parents = WeakKeyDictionary() - # id is an arbitrary integer that is updated every time the node - # is invalidated. - self.id = TransformNode._gid - TransformNode._gid += 1 - # TransformNodes start out as invalid until their values are # computed for the first time. self._invalid = 1 @@ -105,15 +98,14 @@ value = ((self.is_affine or self.is_bbox) and self.INVALID_AFFINE or self.INVALID) - + # Invalidate all ancestors of self using pseudo-recursion. + parent = None stack = [self] while len(stack): root = stack.pop() # Stop at subtrees that have already been invalidated if root._invalid == 0 or root.pass_through: - root.id = TransformNode._gid - TransformNode._gid += 1 root._invalid = value stack.extend(root._parents.keys()) @@ -466,8 +458,7 @@ count = 0 for bbox in bboxes: - # bx1, by1, bx2, by2 = bbox._get_lbrt() - # The above, inlined... + # bx1, by1, bx2, by2 = bbox._get_lbrt() ... inlined... bx1, by1, bx2, by2 = bbox.get_points().flatten() if bx2 < bx1: bx2, bx1 = bx1, bx2 @@ -497,6 +488,26 @@ """ return Bbox(self._points + (tx, ty)) + def corners(self): + """ + Return an array of points which are the four corners of this + rectangle. + """ + l, b, r, t = self.get_points().flatten() + return npy.array([[l, b], [l, t], [r, b], [r, t]]) + + def rotated(self, radians): + """ + Return a new bounding box that bounds a rotated version of this + bounding box. The new bounding box is still aligned with the + axes, of course. + """ + corners = self.corners() + corners_rotated = Affine2D().rotate(radians).transform(corners) + bbox = Bbox.unit() + bbox.update_from_data(corners_rotated, ignore=True) + return bbox + #@staticmethod def union(bboxes): """ @@ -965,7 +976,6 @@ self.transform_path_non_affine = child.transform_path_non_affine self.get_affine = child.get_affine self.inverted = child.inverted - # self.get_matrix = child.get_matrix def set(self, child): """ @@ -1609,7 +1619,8 @@ self._x = x_transform self._y = y_transform self.set_children(x_transform, y_transform) - + self._affine = None + def _get_is_affine(self): return self._x.is_affine and self._y.is_affine is_affine = property(_get_is_affine) @@ -1648,9 +1659,7 @@ transform.__doc__ = Transform.transform.__doc__ def transform_affine(self, points): - if self._x.is_affine and self._y.is_affine: - return self.transform(points) - return points + return self.get_affine().transform(points) transform_affine.__doc__ = Transform.transform_affine.__doc__ def transform_non_affine(self, points): @@ -1664,18 +1673,22 @@ inverted.__doc__ = Transform.inverted.__doc__ def get_affine(self): - if self._x.is_affine and self._y.is_affine: - if self._x == self._y: - return self._x.get_affine() + if self._invalid or self._affine is None: + if self._x.is_affine and self._y.is_affine: + if self._x == self._y: + self._affine = self._x.get_affine() + else: + x_mtx = self._x.get_affine().get_matrix() + y_mtx = self._y.get_affine().get_matrix() + # This works because we already know the transforms are + # separable, though normally one would want to set b and + # c to zero. + mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) + self._affine = Affine2D(mtx) else: - x_mtx = self._x.get_affine().get_matrix() - y_mtx = self._y.get_affine().get_matrix() - # This works because we already know the transforms are - # separable, though normally one would want to set b and - # c to zero. - mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) - return Affine2D(mtx) - return IdentityTransform() + self._affine = IdentityTransform() + self._invalid = 0 + return self._affine get_affine.__doc__ = Transform.get_affine.__doc__ @@ -1830,6 +1843,7 @@ self._b = b self.set_children(a, b) self._mtx = None + self._affine = None def frozen(self): self._invalid = 0 @@ -1857,8 +1871,7 @@ transform.__doc__ = Transform.transform.__doc__ def transform_affine(self, points): - return self._b.transform_affine( - self._a.transform(points)) + return self.get_affine().transform(points) transform_affine.__doc__ = Transform.transform_affine.__doc__ def transform_non_affine(self, points): @@ -1886,10 +1899,14 @@ transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ def get_affine(self): - if self._a.is_affine and self._b.is_affine: - return Affine2D(npy.dot(self._b.get_affine().get_matrix(), - self._a.get_affine().get_matrix())) - return self._b.get_affine() + if self._invalid or self._affine is None: + if self._a.is_affine and self._b.is_affine: + self._affine = Affine2D(npy.dot(self._b.get_affine().get_matrix(), + self._a.get_affine().get_matrix())) + else: + self._affine = self._b.get_affine() + self._invalid = 0 + return self._affine get_affine.__doc__ = Transform.get_affine.__doc__ def inverted(self): @@ -2023,6 +2040,7 @@ self._transform = transform self.set_children(transform) self._transformed_path = None + self.get_affine = self._transform.get_affine def get_transformed_path_and_affine(self): """ @@ -2035,7 +2053,7 @@ self._transformed_path = \ self._transform.transform_path_non_affine(self._path) self._invalid = 0 - return self._transformed_path, self._transform.get_affine() + return self._transformed_path, self.get_affine() def get_fully_transformed_path(self): """ @@ -2047,12 +2065,6 @@ self._transform.transform_path_non_affine(self._path) self._invalid = 0 return self._transform.transform_path_affine(self._transformed_path) - - def get_affine(self): - """ - Get the affine part of the child transform. - """ - return self._transform.get_affine() def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |