From: <md...@us...> - 2007-10-12 17:30:22
|
Revision: 3939 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3939&view=rev Author: mdboom Date: 2007-10-12 10:30:17 -0700 (Fri, 12 Oct 2007) Log Message: ----------- More progress on examples. Modified Paths: -------------- branches/transforms/PASSED_DEMOS branches/transforms/examples/collections_demo.py branches/transforms/examples/simple_plot_fps.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/colorbar.py branches/transforms/lib/matplotlib/contour.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/ticker.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp Modified: branches/transforms/PASSED_DEMOS =================================================================== --- branches/transforms/PASSED_DEMOS 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/PASSED_DEMOS 2007-10-12 17:30:17 UTC (rev 3939) @@ -16,10 +16,10 @@ arctest.py O arrow_demo.py O axes_demo.py O -axes_props.py [SOMETHING FUNNY ABOUT DASHED LINES] +axes_props.py O axhspan_demo.py O axis_equal_demo.py O -backend_driver.py +backend_driver.py [N/A] barchart_demo.py O barcode_demo.py O barh_demo.py [BROKEN IN TRUNK] @@ -29,14 +29,14 @@ broken_barh.py O clippath_test.py O clippedline.py O -collections_demo.py -- [NEEDS ADDITIONAL WORK] +collections_demo.py O colorbar_only.py O color_by_yvalue.py O color_demo.py O colours.py [???] -contour_demo.py -contourf_demo.py -contour_image.py +contour_demo.py O +contourf_demo.py [FLOATING POINT EXCEPTION] +contour_image.py [FLOATING POINT EXCEPTION] coords_demo.py O coords_report.py O csd_demo.py O Modified: branches/transforms/examples/collections_demo.py =================================================================== --- branches/transforms/examples/collections_demo.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/examples/collections_demo.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -87,7 +87,7 @@ a = fig.add_subplot(2,2,3) col = collections.RegularPolyCollection(fig.dpi, 7, - sizes = N.fabs(xx) / 10.0, offsets=xyo, + sizes = N.fabs(xx)*10.0, offsets=xyo, transOffset=a.transData) trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform Modified: branches/transforms/examples/simple_plot_fps.py =================================================================== --- branches/transforms/examples/simple_plot_fps.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/examples/simple_plot_fps.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -21,11 +21,13 @@ #savefig('simple_plot') import time - +from matplotlib import transforms + frames = 100.0 t = time.clock() ion() for i in xrange(int(frames)): + transforms.CATCH = True part = i / frames axis([0.0, 1.0 - part, -1.0 + part, 1.0 - part]) show() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -1110,7 +1110,6 @@ # 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]) Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -91,16 +91,18 @@ 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() + self.gridline = self._get_gridline() - - self.tick1line = self._get_tick1line(loc) - self.tick2line = self._get_tick2line(loc) - self.gridline = self._get_gridline(loc) - - self.label1 = self._get_text1(loc) + self.label1 = self._get_text1() self.label = self.label1 # legacy name - self.label2 = self._get_text2(loc) + self.label2 = self._get_text2() + self.update_position(loc) + self.gridOn = gridOn self.tick1On = tick1On self.tick2On = tick2On @@ -138,23 +140,23 @@ 'Get the value of the tick label pad in points' return self._pad.get() - def _get_text1(self, loc): + def _get_text1(self): 'Get the default Text 1 instance' pass - def _get_text2(self, loc): + def _get_text2(self): 'Get the default Text 2 instance' pass - def _get_tick1line(self, loc): + def _get_tick1line(self): 'Get the default line2D instance for tick1' pass - def _get_tick2line(self, loc): + def _get_tick2line(self): 'Get the default line2D instance for tick2' pass - def _get_gridline(self, loc): + def _get_gridline(self): 'Get the default grid Line2d instance for this tick' pass @@ -183,22 +185,6 @@ renderer.close_group(self.__name__) - def set_xy(self, loc): - """ - Set the location of tick in data coords with scalar loc - - ACCEPTS: float - """ - raise NotImplementedError('Derived must override') - - def set_label(self, s): # legacy name - """ - Set the text of ticklabel - - ACCEPTS: str - """ - self.label1.set_text(s) - def set_label1(self, s): """ Set the text of ticklabel @@ -206,7 +192,8 @@ ACCEPTS: str """ self.label1.set_text(s) - + set_label = set_label1 + def set_label2(self, s): """ Set the text of ticklabel2 @@ -233,7 +220,7 @@ the label text and the grid line """ __name__ = 'xtick' - def _get_text1(self, loc): + def _get_text1(self): 'Get the default Text instance' # the y loc is 3 points below the min of y axis # get the affine as an a,b,c,d,tx,ty list @@ -242,7 +229,7 @@ trans, vert, horiz = self.axes.get_xaxis_text1_transform(self._padPixels) t = TextWithDash( - x=loc, y=0, + x=0, y=0, fontproperties=FontProperties(size=rcParams['xtick.labelsize']), color=rcParams['xtick.color'], verticalalignment=vert, @@ -256,15 +243,15 @@ return t - def _get_text2(self, loc): + def _get_text2(self): 'Get the default Text 2 instance' # x in data coords, y in axes coords #t = Text( trans, vert, horiz = self.axes.get_xaxis_text2_transform(self._padPixels) - t = TextWithDash( - x=loc, y=1, + t = TextWithDash( + x=0, y=1, fontproperties=FontProperties(size=rcParams['xtick.labelsize']), color=rcParams['xtick.color'], verticalalignment=vert, @@ -272,60 +259,55 @@ xaxis=True, horizontalalignment=horiz, ) - t.set_transform(trans) self._set_artist_props(t) return t - def _get_tick1line(self, loc): + def _get_tick1line(self): 'Get the default line2D instance' # x in data coords, y in axes coords - l = Line2D( xdata=(loc,), ydata=(0,), - color='k', - linestyle = 'None', - marker = self._xtickmarkers[0], - markersize=self._size, - ) + l = Line2D(xdata=(0,), ydata=(0,), + color='k', + linestyle = 'None', + marker = self._xtickmarkers[0], + markersize=self._size, + ) l.set_transform(self.axes.get_xaxis_transform()) self._set_artist_props(l) return l - def _get_tick2line(self, loc): + def _get_tick2line(self): 'Get the default line2D instance' # x in data coords, y in axes coords - l = Line2D( xdata=(loc,), ydata=(1,), + l = Line2D( xdata=(0,), ydata=(1,), color='k', linestyle = 'None', marker = self._xtickmarkers[1], markersize=self._size, ) - l.set_transform(self.axes.get_xaxis_transform()) + l.set_transform(self._locTransform + self.axes.get_xaxis_transform()) self._set_artist_props(l) return l - def _get_gridline(self, loc): + def _get_gridline(self): 'Get the default line2D instance' # x in data coords, y in axes coords - l = Line2D(xdata=(loc, loc), ydata=(0, 1.0), + l = Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0), color=rcParams['grid.color'], linestyle=rcParams['grid.linestyle'], linewidth=rcParams['grid.linewidth'], ) - l.set_transform(self.axes.get_xaxis_transform()) + l.set_transform(self._locTransform + 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' - 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._locTransform.clear().translate(loc, 0.0) + self.label1.set_x(loc) + self.label2.set_x(loc) self._loc = loc def get_view_interval(self): @@ -355,14 +337,14 @@ __name__ = 'ytick' # how far from the y axis line the right of the ticklabel are - def _get_text1(self, loc): + def _get_text1(self): 'Get the default Text instance' # x in axes coords, y in data coords #t = Text( trans, vert, horiz = self.axes.get_yaxis_text1_transform(self._padPixels) t = TextWithDash( - x=0, y=loc, + x=0, y=0, fontproperties=FontProperties(size=rcParams['ytick.labelsize']), color=rcParams['ytick.color'], verticalalignment=vert, @@ -375,14 +357,14 @@ self._set_artist_props(t) return t - def _get_text2(self, loc): + def _get_text2(self): 'Get the default Text instance' # x in axes coords, y in data coords #t = Text( trans, vert, horiz = self.axes.get_yaxis_text2_transform(self._padPixels) - t = TextWithDash( - x=1, y=loc, + t = TextWithDash( + x=1, y=0, fontproperties=FontProperties(size=rcParams['ytick.labelsize']), color=rcParams['ytick.color'], verticalalignment=vert, @@ -394,20 +376,20 @@ self._set_artist_props(t) return t - def _get_tick1line(self, loc): + def _get_tick1line(self): 'Get the default line2D instance' # x in axes coords, y in data coords - l = Line2D( (0,), (loc,), color='k', + l = Line2D( (0,), (0,), color='k', marker = self._ytickmarkers[0], linestyle = 'None', markersize=self._size, ) - l.set_transform(self.axes.get_yaxis_transform()) + l.set_transform(self._locTransform + self.axes.get_yaxis_transform()) self._set_artist_props(l) return l - def _get_tick2line(self, loc): + def _get_tick2line(self): 'Get the default line2D instance' # x in axes coords, y in data coords l = Line2D( (1,), (0,), color='k', @@ -416,34 +398,29 @@ markersize=self._size, ) - l.set_transform(self.axes.get_yaxis_transform()) + l.set_transform(self._locTransform + self.axes.get_yaxis_transform()) self._set_artist_props(l) return l - def _get_gridline(self, loc): + def _get_gridline(self): 'Get the default line2D instance' # x in axes coords, y in data coords - l = Line2D( xdata=(0,1), ydata=(loc,loc), + l = Line2D( xdata=(0,1), ydata=(0, 0), color=rcParams['grid.color'], linestyle=rcParams['grid.linestyle'], linewidth=rcParams['grid.linewidth'], ) - l.set_transform(self.axes.get_yaxis_transform()) + l.set_transform(self._locTransform + 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' - 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._locTransform.clear().translate(0.0, loc) + self.label1.set_y(loc) + self.label2.set_y(loc) self._loc = loc @@ -558,9 +535,11 @@ popall(self.majorTicks) popall(self.minorTicks) - self.majorTicks.extend([self._get_tick(major=True) for i in range(1)]) - self.minorTicks.extend([self._get_tick(major=False) for i in range(1)]) - + self.majorTicks.extend([self._get_tick(major=True)]) + self.minorTicks.extend([self._get_tick(major=False)]) + self._lastNumMajorTicks = 1 + self._lastNumMinorTicks = 1 + self.converter = None self.units = None self.set_units(None) @@ -744,16 +723,21 @@ 'get the tick instances; grow as necessary' if numticks is None: numticks = len(self.get_major_locator()()) - - if len(self.majorTicks)<numticks: + + if len(self.majorTicks) < numticks: # update the new tick label properties from the old - protoTick = self.majorTicks[0] - for i in range(numticks-len(self.majorTicks)): + for i in range(numticks - len(self.majorTicks)): tick = self._get_tick(major=True) - #tick = protoTick + self.majorTicks.append(tick) + + if self._lastNumMajorTicks < numticks: + protoTick = self.majorTicks[0] + for i in range(self._lastNumMajorTicks, len(self.majorTicks)): + tick = self.majorTicks[i] if self._gridOnMajor: tick.gridOn = True self._copy_tick_props(protoTick, tick) - self.majorTicks.append(tick) + + self._lastNumMajorTicks = numticks ticks = self.majorTicks[:numticks] return ticks @@ -764,13 +748,20 @@ if numticks is None: numticks = len(self.get_minor_locator()()) - if len(self.minorTicks)<numticks: + 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) + self.minorTicks.append(tick) + + if self._lastNumMinorTicks < numticks: protoTick = self.minorTicks[0] - for i in range(numticks-len(self.minorTicks)): - tick = self._get_tick(major=False) + for i in range(self._lastNumMinorTicks, len(self.minorTicks)): + tick = self.minorTicks[i] if self._gridOnMinor: tick.gridOn = True self._copy_tick_props(protoTick, tick) - self.minorTicks.append(tick) + + self._lastNumMinorTicks = numticks ticks = self.minorTicks[:numticks] return ticks @@ -946,7 +937,6 @@ self.set_major_formatter( FixedFormatter(ticklabels) ) - ret = [] for i, tick in enumerate(self.get_major_ticks()): if i<len(ticklabels): Modified: branches/transforms/lib/matplotlib/colorbar.py =================================================================== --- branches/transforms/lib/matplotlib/colorbar.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/colorbar.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -314,8 +314,7 @@ b = self._boundaries[self._inside] locator = ticker.FixedLocator(b, nbins=10) if isinstance(self.norm, colors.NoNorm): - intv = transforms.Interval(transforms.Value(self._values[0]), - transforms.Value(self._values[-1])) + intv = self._values[0], self._values[-1] else: intv = self.vmin, self.vmax locator.create_dummy_axis() Modified: branches/transforms/lib/matplotlib/contour.py =================================================================== --- branches/transforms/lib/matplotlib/contour.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/contour.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -8,6 +8,7 @@ import numpy as npy import matplotlib.numerix.npyma as ma import matplotlib._cntr as _cntr +import matplotlib.path as path import matplotlib.ticker as ticker import matplotlib.transforms as transforms import matplotlib.cm as cm @@ -210,7 +211,7 @@ trans = self.ax.transData - slc = trans.seq_xy_tups(linecontour) + slc = trans.transform(linecontour) x,y = slc[ind] xx= npy.asarray(slc)[:,0].copy() yy=npy.asarray(slc)[:,1].copy() @@ -247,8 +248,9 @@ new_x1, new_y1 = x-xlabel, y-ylabel new_x2, new_y2 = x+xlabel, y+ylabel - new_x1d, new_y1d = trans.inverse_xy_tup((new_x1, new_y1)) - new_x2d, new_y2d = trans.inverse_xy_tup((new_x2, new_y2)) + inverse = trans.inverted() + new_x1d, new_y1d = inverse.transform_point((new_x1, new_y1)) + new_x2d, new_y2d = inverse.transform_point((new_x2, new_y2)) new_xy1 = npy.array(((new_x1d, new_y1d),)) new_xy2 = npy.array(((new_x2d, new_y2d),)) @@ -333,20 +335,22 @@ con = self.collections[icon] lw = self.get_label_width(lev, fmt, fsize) additions = [] - for segNum, linecontour in enumerate(con._segments): + paths = con.get_paths() + for segNum, linepath in enumerate(paths): + linecontour = linepath.vertices # for closed contours add one more point to # avoid division by zero if npy.all(linecontour[0] == linecontour[-1]): linecontour = npy.concatenate((linecontour, - linecontour[1][npy.newaxis,:])) + linecontour[1][npy.newaxis,:])) #linecontour.append(linecontour[1]) # transfer all data points to screen coordinates - slc = trans.seq_xy_tups(linecontour) + slc = trans.transform(linecontour) if self.print_label(slc,lw): x,y, rotation, ind = self.locate_label(slc, lw) # transfer the location of the label back to # data coordinates - dx,dy = trans.inverse_xy_tup((x,y)) + dx,dy = trans.inverted().transform_point((x,y)) t = text.Text(dx, dy, rotation = rotation, horizontalalignment='center', verticalalignment='center') @@ -355,12 +359,14 @@ self.cl.append(t) self.cl_cvalues.append(cvalue) if inline: - new = self.break_linecontour(linecontour, rotation, - lw, ind) - con._segments[segNum] = new[0] - additions.append(new[1]) - con._segments.extend(additions) + new = self.break_linecontour(linecontour, rotation, lw, ind) + if len(new[0]): + paths[segNum] = path.Path(new[0], closed=False) + if len(new[1]): + additions.append(path.Path(new[1], closed=False)) + paths.extend(additions) + class ContourSet(cm.ScalarMappable, ContourLabeler): """ Create and store a set of contour lines or filled regions. @@ -494,6 +500,7 @@ ''' if self.locator is None: self.locator = ticker.MaxNLocator(N+1) + self.locator.create_dummy_axis() locator = self.locator zmax = self.zmax zmin = self.zmin Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/text.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -172,7 +172,6 @@ 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 @@ -327,11 +326,10 @@ 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)) - if renderer.flipy(): - canvasw, canvash = renderer.get_canvas_width_height() y = canvash-y renderer.draw_text(gc, x, y, line, Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/ticker.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -145,6 +145,11 @@ def set_data_interval(self, vmin, vmax): self.axis.set_data_interval(vmin, vmax) + + def set_bounds(self, vmin, vmax): + self.set_view_interval(vmin, vmax) + self.set_data_interval(vmin, vmax) + class Formatter(TickHelper): """ Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-12 17:30:17 UTC (rev 3939) @@ -32,8 +32,10 @@ import cbook from path import Path -DEBUG = True +DEBUG = False +# MGDTODO: Cache get_affine??? + class TransformNode(object): """ TransformNode is the base class for anything that participates in @@ -81,7 +83,7 @@ # A list of the children is kept around for debugging purposes # only. self._children = [] - + def __copy__(self, *args): raise NotImplementedError( "TransformNode instances can not be copied. " + @@ -402,13 +404,13 @@ self._points[0] + (W, H)]) def splitx(self, *args): - ''' + """ e.g., bbox.splitx(f1, f2, ...) Returns a list of new BBoxes formed by splitting the original one with vertical lines at fractional positions f1, f2, ... - ''' + """ boxes = [] xf = [0] + list(args) + [1] l, b, r, t = self.lbrt @@ -418,13 +420,13 @@ return boxes def splity(self, *args): - ''' + """ e.g., bbox.splitx(f1, f2, ...) Returns a list of new PBoxes formed by splitting the original one with horizontal lines at fractional positions f1, f2, ... - ''' + """ boxes = [] yf = [0] + list(args) + [1] l, b, r, t = self.lbrt @@ -434,6 +436,11 @@ return boxes def count_contains(self, vertices): + """ + Count the number of vertices contained in the Bbox. + + vertices is a Nx2 numpy array. + """ if len(vertices) == 0: return 0 vertices = npy.asarray(vertices) @@ -446,6 +453,11 @@ return N.sum(inside) def count_overlaps(self, bboxes): + """ + Count the number of bounding boxes that overlap this one. + + bboxes is a sequence of Bbox objects + """ ax1, ay1, ax2, ay2 = self._get_lbrt() if ax2 < ax1: ax2, ax1 = ax1, ax2 @@ -454,7 +466,9 @@ count = 0 for bbox in bboxes: - bx1, by1, bx2, by2 = bbox._get_lbrt() + # bx1, by1, bx2, by2 = bbox._get_lbrt() + # The above, inlined... + bx1, by1, bx2, by2 = bbox.get_points().flatten() if bx2 < bx1: bx2, bx1 = bx1, bx2 if by2 < by1: @@ -464,7 +478,53 @@ (bx1 >= ax2) or (by1 >= ay2))) return count - + + def expanded(self, sw, sh): + """ + Return a new Bbox which is this Bbox expanded around its + center by the given factors sw and sh. + """ + width = self.width + height = self.height + deltaw = (sw * width - width) / 2.0 + deltah = (sh * height - height) / 2.0 + a = npy.array([[-deltaw, -deltah], [deltaw, deltah]]) + return Bbox(self._points + a) + + def translated(self, tx, ty): + """ + Return a copy of the Bbox, translated by tx and ty. + """ + return Bbox(self._points + (tx, ty)) + + #@staticmethod + def union(bboxes): + """ + Return a Bbox that contains all of the given bboxes. + """ + assert(len(bboxes)) + + if len(bboxes) == 1: + return bboxes[0] + + xmin = npy.inf + ymin = npy.inf + xmax = -npy.inf + ymax = -npy.inf + + for bbox in bboxes: + points = bbox.get_points() + xs = points[:, 0] + ys = points[:, 1] + xmin = min(xmin, npy.min(xs)) + ymin = min(ymin, npy.min(ys)) + xmax = max(xmax, npy.max(xs)) + ymax = max(ymax, npy.max(ys)) + + return Bbox.from_lbrt(xmin, ymin, xmax, ymax) + union = staticmethod(union) + + class Bbox(BboxBase): def __init__(self, points): """ @@ -535,7 +595,6 @@ when None, use the last value passed to Bbox.ignore(). """ # MGDTODO: It may be more efficient for some callers to use update_from_data_xy instead - if ignore is None: ignore = self._ignore @@ -569,7 +628,6 @@ max(y.max(), self.ymax)]], npy.float_) self._minpos = npy.minimum(minpos, self._minpos) - self.invalidate() def update_from_data_xy(self, xy, ignore=None): @@ -665,52 +723,7 @@ """ self._points = other.get_points() self.invalidate() - - def expanded(self, sw, sh): - """ - Return a new Bbox which is this Bbox expanded around its - center by the given factors sw and sh. - """ - width = self.width - height = self.height - deltaw = (sw * width - width) / 2.0 - deltah = (sh * height - height) / 2.0 - a = npy.array([[-deltaw, -deltah], [deltaw, deltah]]) - return Bbox(self._points + a) - def translated(self, tx, ty): - """ - Return a copy of the Bbox, translated by tx and ty. - """ - return Bbox(self._points + (tx, ty)) - - #@staticmethod - def union(bboxes): - """ - Return a Bbox that contains all of the given bboxes. - """ - assert(len(bboxes)) - - if len(bboxes) == 1: - return bboxes[0] - - xmin = npy.inf - ymin = npy.inf - xmax = -npy.inf - ymax = -npy.inf - - for bbox in bboxes: - points = bbox.get_points() - xs = points[:, 0] - ys = points[:, 1] - xmin = min(xmin, npy.min(xs)) - ymin = min(ymin, npy.min(ys)) - xmax = max(xmax, npy.max(xs)) - ymax = max(ymax, npy.max(ys)) - - return Bbox.from_lbrt(xmin, ymin, xmax, ymax) - union = staticmethod(union) - class TransformedBbox(BboxBase): """ @@ -1633,15 +1646,39 @@ else: return npy.concatenate((x_points, y_points), 1) transform.__doc__ = Transform.transform.__doc__ - - transform_non_affine = transform + + def transform_affine(self, points): + if self._x.is_affine and self._y.is_affine: + return self.transform(points) + return points + transform_affine.__doc__ = Transform.transform_affine.__doc__ + + def transform_non_affine(self, points): + if self._x.is_affine and self._y.is_affine: + return points + return self.transform(points) transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ def inverted(self): return BlendedGenericTransform(self._x.inverted(), self._y.inverted()) 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() + 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() + get_affine.__doc__ = Transform.get_affine.__doc__ + + class BlendedAffine1D(Affine2DBase): """ A "blended" transform uses one transform for the x-direction, and @@ -1807,7 +1844,7 @@ is_affine = property(_get_is_affine) def _get_is_separable(self): - return self._a.is_separable() and self._b.is_separable() + return self._a.is_separable and self._b.is_separable is_separable = property(_get_is_separable) def __repr__(self): @@ -1838,7 +1875,7 @@ def transform_path_affine(self, path): return self._b.transform_path_affine( - self._a.transform(path)) + self._a.transform_path(path)) transform_path_affine.__doc__ = Transform.transform_path_affine.__doc__ def transform_path_non_affine(self, path): @@ -1850,7 +1887,8 @@ def get_affine(self): if self._a.is_affine and self._b.is_affine: - return CompositeAffine2D(self._a.get_affine(), self._b.get_affine()) + return Affine2D(npy.dot(self._b.get_affine().get_matrix(), + self._a.get_affine().get_matrix())) return self._b.get_affine() get_affine.__doc__ = Transform.get_affine.__doc__ Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-12 14:29:57 UTC (rev 3938) +++ branches/transforms/src/_backend_agg.cpp 2007-10-12 17:30:17 UTC (rev 3939) @@ -82,7 +82,10 @@ return agg::trans_affine(a, b, c, d, e, f); } } catch (...) { - + if (errors) { + Py_XDECREF(matrix); + throw; + } } Py_XDECREF(matrix); @@ -333,8 +336,10 @@ bool RendererAgg::bbox_to_rect(const Py::Object& bbox_obj, double* l, double* b, double* r, double* t) { + PyArrayObject* bbox = NULL; + if (bbox_obj.ptr() != Py_None) { - PyArrayObject* bbox = (PyArrayObject*) PyArray_FromObject(bbox_obj.ptr(), PyArray_DOUBLE, 2, 2); + bbox = (PyArrayObject*) PyArray_FromObject(bbox_obj.ptr(), PyArray_DOUBLE, 2, 2); if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) { Py_XDECREF(bbox); @@ -559,9 +564,9 @@ PathIterator path(path_obj); transformed_path_t path_transformed(path, trans); - bool snap = should_snap(path, trans); - GCAgg gc = GCAgg(gc_obj, dpi, snap); - quantize_t path_quantized(path_transformed, snap); + // bool snap = should_snap(path, trans); + GCAgg gc = GCAgg(gc_obj, dpi, true); + quantize_t path_quantized(path_transformed, true); path_quantized.rewind(0); facepair_t face = _get_rgba_face(face_obj, gc.alpha); @@ -638,6 +643,7 @@ } catch(...) { delete[] fillCache; delete[] strokeCache; + throw; } delete [] fillCache; @@ -879,7 +885,7 @@ theRasterizer->add_path(stroke); } - if (gc.isaa && !(snap && gc.dashes.size())) { + if (gc.isaa && !(snap && gc.linewidth < 1.5)) { if (has_clippath) { pixfmt_amask_type pfa(*pixFmt, *alphaMask); amask_ren_type r(pfa); @@ -954,99 +960,110 @@ GCAgg gc(dpi, false); - PyArrayObject* offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!offsets || offsets->dimensions[1] != 2) - throw Py::ValueError("Offsets array must be Nx2"); + PyArrayObject* offsets = NULL; + PyArrayObject* facecolors = NULL; + PyArrayObject* edgecolors = NULL; - PyArrayObject* facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); - if (!facecolors || - (facecolors->nd == 1 && facecolors->dimensions[0] != 0) || - (facecolors->nd == 2 && facecolors->dimensions[1] != 4)) - throw Py::ValueError("Facecolors must be a Nx4 numpy array or empty"); + try { + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!offsets || offsets->dimensions[1] != 2) + throw Py::ValueError("Offsets array must be Nx2"); - PyArrayObject* edgecolors = (PyArrayObject*)PyArray_FromObject(edgecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); - if (!edgecolors || - (edgecolors->nd == 1 && edgecolors->dimensions[0] != 0) || - (edgecolors->nd == 2 && edgecolors->dimensions[1] != 4)) - throw Py::ValueError("Edgecolors must be a Nx4 numpy array"); + PyArrayObject* facecolors = (PyArrayObject*)PyArray_FromObject(facecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); + if (!facecolors || + (facecolors->nd == 1 && facecolors->dimensions[0] != 0) || + (facecolors->nd == 2 && facecolors->dimensions[1] != 4)) + throw Py::ValueError("Facecolors must be a Nx4 numpy array or empty"); - size_t Npaths = paths.length(); - size_t Noffsets = offsets->dimensions[0]; - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t Nfacecolors = facecolors->dimensions[0]; - size_t Nedgecolors = edgecolors->dimensions[0]; - size_t Nlinewidths = linewidths.length(); - size_t Nlinestyles = std::min(linestyles_obj.length(), N); - size_t Naa = antialiaseds.length(); + PyArrayObject* edgecolors = (PyArrayObject*)PyArray_FromObject(edgecolors_obj.ptr(), PyArray_DOUBLE, 1, 2); + if (!edgecolors || + (edgecolors->nd == 1 && edgecolors->dimensions[0] != 0) || + (edgecolors->nd == 2 && edgecolors->dimensions[1] != 4)) + throw Py::ValueError("Edgecolors must be a Nx4 numpy array"); + + size_t Npaths = paths.length(); + size_t Noffsets = offsets->dimensions[0]; + size_t N = std::max(Npaths, Noffsets); + size_t Ntransforms = std::min(transforms_obj.length(), N); + size_t Nfacecolors = facecolors->dimensions[0]; + size_t Nedgecolors = edgecolors->dimensions[0]; + size_t Nlinewidths = linewidths.length(); + size_t Nlinestyles = std::min(linestyles_obj.length(), N); + size_t Naa = antialiaseds.length(); - if ((Nfacecolors == 0 && Nedgecolors == 0) || N == 0) - return Py::Object(); - - size_t i = 0; - - // Convert all of the transforms up front - typedef std::vector<agg::trans_affine> transforms_t; - transforms_t transforms; - transforms.reserve(Ntransforms); - for (i = 0; i < Ntransforms; ++i) { - agg::trans_affine trans = py_to_agg_transformation_matrix - (transforms_obj[i], false); - trans *= master_transform; - transforms.push_back(trans); - } - - // Convert all the dashes up front - typedef std::vector<std::pair<double, GCAgg::dash_t> > dashes_t; - dashes_t dashes; - dashes.resize(Nlinestyles); - i = 0; - for (dashes_t::iterator d = dashes.begin(); - d != dashes.end(); ++d, ++i) { - convert_dashes(Py::Tuple(linestyles_obj[i]), false, dpi, d->second, d->first); - } - - // Handle any clipping globally - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); - set_clipbox(cliprect, theRasterizer); - bool has_clippath = render_clippath(clippath, clippath_trans); - - // Set some defaults, assuming no face or edge - gc.linewidth = 0.0; - facepair_t face; - face.first = Nfacecolors != 0; - - for (i = 0; i < N; ++i) { - PathIterator path(paths[i % Npaths]); - bool snap = (path.total_vertices() == 2); - double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); - double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); - offset_trans.transform(&xo, &yo); - agg::trans_affine_translation transOffset(xo, yo); - agg::trans_affine& trans = transforms[i % Ntransforms]; - - if (Nfacecolors) { - size_t fi = i % Nfacecolors; - face.second = agg::rgba(*(double*)PyArray_GETPTR2(facecolors, fi, 0), - *(double*)PyArray_GETPTR2(facecolors, fi, 1), - *(double*)PyArray_GETPTR2(facecolors, fi, 2), - *(double*)PyArray_GETPTR2(facecolors, fi, 3)); + if ((Nfacecolors == 0 && Nedgecolors == 0) || N == 0) + return Py::Object(); + + size_t i = 0; + + // Convert all of the transforms up front + typedef std::vector<agg::trans_affine> transforms_t; + transforms_t transforms; + transforms.reserve(Ntransforms); + for (i = 0; i < Ntransforms; ++i) { + agg::trans_affine trans = py_to_agg_transformation_matrix + (transforms_obj[i], false); + trans *= master_transform; + transforms.push_back(trans); } - - if (Nedgecolors) { - size_t ei = i % Nedgecolors; - gc.color = agg::rgba(*(double*)PyArray_GETPTR2(edgecolors, ei, 0), - *(double*)PyArray_GETPTR2(edgecolors, ei, 1), - *(double*)PyArray_GETPTR2(edgecolors, ei, 2), - *(double*)PyArray_GETPTR2(edgecolors, ei, 3)); - gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; - gc.dashes = dashes[i % Nlinestyles].second; - gc.dashOffset = dashes[i % Nlinestyles].first; + + // Convert all the dashes up front + typedef std::vector<std::pair<double, GCAgg::dash_t> > dashes_t; + dashes_t dashes; + dashes.resize(Nlinestyles); + i = 0; + for (dashes_t::iterator d = dashes.begin(); + d != dashes.end(); ++d, ++i) { + convert_dashes(Py::Tuple(linestyles_obj[i]), false, dpi, d->second, d->first); } - - gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); - _draw_path(path, trans * transOffset, snap, has_clippath, face, gc); + + // Handle any clipping globally + theRasterizer->reset_clipping(); + rendererBase->reset_clipping(true); + set_clipbox(cliprect, theRasterizer); + bool has_clippath = render_clippath(clippath, clippath_trans); + + // Set some defaults, assuming no face or edge + gc.linewidth = 0.0; + facepair_t face; + face.first = Nfacecolors != 0; + + for (i = 0; i < N; ++i) { + PathIterator path(paths[i % Npaths]); + bool snap = (path.total_vertices() == 2); + double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); + double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); + offset_trans.transform(&xo, &yo); + agg::trans_affine_translation transOffset(xo, yo); + agg::trans_affine& trans = transforms[i % Ntransforms]; + + if (Nfacecolors) { + size_t fi = i % Nfacecolors; + face.second = agg::rgba(*(double*)PyArray_GETPTR2(facecolors, fi, 0), + *(double*)PyArray_GETPTR2(facecolors, fi, 1), + *(double*)PyArray_GETPTR2(facecolors, fi, 2), + *(double*)PyArray_GETPTR2(facecolors, fi, 3)); + } + + if (Nedgecolors) { + size_t ei = i % Nedgecolors; + gc.color = agg::rgba(*(double*)PyArray_GETPTR2(edgecolors, ei, 0), + *(double*)PyArray_GETPTR2(edgecolors, ei, 1), + *(double*)PyArray_GETPTR2(edgecolors, ei, 2), + *(double*)PyArray_GETPTR2(edgecolors, ei, 3)); + gc.linewidth = double(Py::Float(linewidths[i % Nlinewidths])) * dpi/72.0; + gc.dashes = dashes[i % Nlinestyles].second; + gc.dashOffset = dashes[i % Nlinestyles].first; + } + + gc.isaa = bool(Py::Int(antialiaseds[i % Naa])); + _draw_path(path, trans * transOffset, snap, has_clippath, face, gc); + } + } catch (...) { + Py_XDECREF(offsets); + Py_XDECREF(facecolors); + Py_XDECREF(edgecolors); + throw; } Py_XDECREF(offsets); @@ -1518,18 +1535,15 @@ void get_path_extents(PathIterator& path, agg::trans_affine& trans, double* x0, double* y0, double* x1, double* y1) { - typedef agg::conv_curve<PathIterator> curve_t; - - curve_t curved_path(path); + typedef agg::conv_transform<PathIterator> transformed_path_t; + typedef agg::conv_curve<transformed_path_t> curve_t; double x, y; - curved_path.rewind(0); + unsigned code; - unsigned code = curved_path.vertex(&x, &y); + transformed_path_t tpath(path, trans); + curve_t curved_path(tpath); - *x0 = x; - *y0 = y; - *x1 = x; - *y1 = y; + curved_path.rewind(0); while ((code = curved_path.vertex(&x, &y)) != agg::path_cmd_stop) { if (code & agg::path_cmd_end_poly == agg::path_cmd_end_poly) @@ -1539,9 +1553,6 @@ if (x > *x1) *x1 = x; if (y > *y1) *y1 = y; } - - trans.transform(x0, y0); - trans.transform(x1, y1); } Py::Object _backend_agg_module::get_path_extents(const Py::Tuple& args) { @@ -1550,7 +1561,11 @@ PathIterator path(args[0]); agg::trans_affine trans = py_to_agg_transformation_matrix(args[1]); - double x0, y0, x1, y1; + double x0 = std::numeric_limits<double>::infinity(); + double y0 = std::numeric_limits<double>::infinity(); + double x1 = -std::numeric_limits<double>::infinity(); + double y1 = -std::numeric_limits<double>::infinity(); + ::get_path_extents(path, trans, &x0, &y0, &x1, &y1); Py::Tuple result(4); @@ -1561,10 +1576,6 @@ return result; } -struct PathCollectionExtents { - double x0, y0, x1, y1; -}; - Py::Object _backend_agg_module::get_path_collection_extents(const Py::Tuple& args) { args.verify_length(5); @@ -1572,57 +1583,58 @@ agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[0]); Py::SeqBase<Py::Object> paths = args[1]; Py::SeqBase<Py::Object> transforms_obj = args[2]; - Py::SeqBase<Py::Object> offsets = args[3]; + Py::Object offsets_obj = args[3]; agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[4], false); - size_t Npaths = paths.length(); - size_t Noffsets = offsets.length(); - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t i; + PyArrayObject* offsets = NULL; + double x0, y0, x1, y1; - // Convert all of the transforms up front - typedef std::vector<agg::trans_affine> transforms_t; - transforms_t transforms; - transforms.reserve(Ntransforms); - for (i = 0; i < Ntransforms; ++i) { - agg::trans_affine trans = py_to_agg_transformation_matrix - (transforms_obj[i], false); - trans *= master_transform; - transforms.push_back(trans); - } - - typedef std::vector<PathCollectionExtents> path_extents_t; - path_extents_t path_extents; - path_extents.resize(Npaths); + try { + offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!offsets || offsets->dimensions[1] != 2) + throw Py::ValueError("Offsets array must be Nx2"); - // Get each of the path extents first - i = 0; - for (path_extents_t::iterator p = path_extents.begin(); - p != path_extents.end(); ++p, ++i) { - PathIterator path(paths[i]); - agg::trans_affine& trans = transforms[i % Ntransforms]; - ::get_path_extents(path, trans, &p->x0, &p->y0, &p->x1, &p->y1); - } + size_t Npaths = paths.length(); + size_t Noffsets = offsets->dimensions[0]; + size_t N = std::max(Npaths, Noffsets); + size_t Ntransforms = std::min(transforms_obj.length(), N); + size_t i; - // The offset each of those and collect the mins/maxs - double x0 = std::numeric_limits<double>::infinity(); - double y0 = std::numeric_limits<double>::infinity(); - double x1 = -std::numeric_limits<double>::infinity(); - double y1 = -std::numeric_limits<double>::infinity(); - for (i = 0; i < N; ++i) { - Py::SeqBase<Py::Float> offset = Py::SeqBase<Py::Float>(offsets[i % Noffsets]); - double xo = Py::Float(offset[0]); - double yo = Py::Float(offset[1]); - offset_trans.transform(&xo, &yo); - PathCollectionExtents& ext = path_extents[i % Npaths]; + // Convert all of the transforms up front + typedef std::vector<agg::trans_affine> transforms_t; + transforms_t transforms; + transforms.reserve(Ntransforms); + for (i = 0; i < Ntransforms; ++i) { + agg::trans_affine trans = py_to_agg_transformation_matrix + (transforms_obj[i], false); + trans *= master_transform; + transforms.push_back(trans); + } + + // The offset each of those and collect the mins/maxs + x0 = std::numeric_limits<double>::infinity(); + y0 = std::numeric_limits<double>::infinity(); + x1 = -std::numeric_limits<double>::infinity(); + y1 = -std::numeric_limits<double>::infinity(); + for (i = 0; i < N; ++i) { + PathIterator path(paths[i % Npaths]); + + double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); + double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); + offset_trans.transform(&xo, &yo); + agg::trans_affine_translation transOffset(xo, yo); + agg::trans_affine trans = transforms[i % Ntransforms]; + trans *= transOffset; - x0 = std::min(x0, ext.x0 + xo); - y0 = std::min(y0, ext.y0 + yo); - x1 = std::max(x1, ext.x1 + xo); - y1 = std::max(y1, ext.y1 + yo); + ::get_path_extents(path, trans, &x0, &y0, &x1, &y1); + } + } catch (...) { + Py_XDECREF(offsets); + throw; } + Py_XDECREF(offsets); + Py::Tuple result(4); result[0] = Py::Float(x0); result[1] = Py::Float(y0); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |