From: <md...@us...> - 2007-09-19 16:18:56
|
Revision: 3858 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3858&view=rev Author: mdboom Date: 2007-09-19 09:18:51 -0700 (Wed, 19 Sep 2007) Log Message: ----------- Got steps_demo.py working Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-19 16:18:51 UTC (rev 3858) @@ -40,7 +40,7 @@ def _get_cached_native_path(self, path): native_path = self._native_paths.get(path) if native_path is None: - # print "CACHE MISS", path + print "CACHE MISS", path native_path = self.convert_to_native_path(path) self._native_paths[path] = native_path return native_path Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 16:18:51 UTC (rev 3858) @@ -25,53 +25,6 @@ (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN) = range(8) -def unmasked_index_ranges(mask, compressed = True): - ''' - Calculate the good data ranges in a masked 1-D npy.array, based on mask. - - Returns Nx2 npy.array with each row the start and stop indices - for slices of the compressed npy.array corresponding to each of N - uninterrupted runs of unmasked values. - If optional argument compressed is False, it returns the - start and stop indices into the original npy.array, not the - compressed npy.array. - Returns None if there are no unmasked values. - - Example: - - y = ma.array(npy.arange(5), mask = [0,0,1,0,0]) - #ii = unmasked_index_ranges(y.mask()) - ii = unmasked_index_ranges(ma.getmask(y)) - # returns [[0,2,] [2,4,]] - - y.compressed().filled()[ii[1,0]:ii[1,1]] - # returns npy.array [3,4,] - # (The 'filled()' method converts the masked npy.array to a numerix npy.array.) - - #i0, i1 = unmasked_index_ranges(y.mask(), compressed=False) - i0, i1 = unmasked_index_ranges(ma.getmask(y), compressed=False) - # returns [[0,3,] [2,5,]] - - y.filled()[ii[1,0]:ii[1,1]] - # returns npy.array [3,4,] - - ''' - m = npy.concatenate(((1,), mask, (1,))) - indices = npy.arange(len(mask) + 1) - mdif = m[1:] - m[:-1] - i0 = npy.compress(mdif == -1, indices) - i1 = npy.compress(mdif == 1, indices) - assert len(i0) == len(i1) - if len(i1) == 0: - return None - if not compressed: - return npy.concatenate((i0[:, npy.newaxis], i1[:, npy.newaxis]), axis=1) - seglengths = i1 - i0 - breakpoints = npy.cumsum(seglengths) - ic0 = npy.concatenate(((0,), breakpoints[:-1])) - 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. @@ -117,7 +70,7 @@ '--' : '_draw_dashed', '-.' : '_draw_dash_dot', ':' : '_draw_dotted', - 'steps': '_draw_solid', + 'steps': '_draw_steps', 'None' : '_draw_nothing', ' ' : '_draw_nothing', '' : '_draw_nothing', @@ -394,6 +347,8 @@ self._yorig = y self.recache() + _masked_array_to_path_code_mapping = npy.array( + [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type) def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units @@ -411,36 +366,28 @@ if len(x) != len(y): raise RuntimeError('xdata and ydata must be the same length') - # MGDTODO: Deal with segments + self._xy = npy.vstack((npy.asarray(x, npy.float_), + npy.asarray(y, npy.float_))).transpose() + self._x = self._xy[:, 0] # just a view + self._y = self._xy[:, 1] # just a view + self._logcache = None + mx = ma.getmask(x) my = ma.getmask(y) mask = ma.mask_or(mx, my) + codes = None if mask is not ma.nomask: - x = ma.masked_array(x, mask=mask).compressed() - y = ma.masked_array(y, mask=mask).compressed() - self._segments = unmasked_index_ranges(mask) - else: - self._segments = None - - self._xy = npy.vstack((npy.asarray(x, npy.float_), - npy.asarray(y, npy.float_))).transpose() - self._x = self._xy[:, 0] - self._y = self._xy[:, 1] - self._logcache = None + m = npy.concatenate(((1,), mask, (1,))) + mdif = m[1:] - m[:-1] + mdif = npy.maximum((mdif[:-1] * -2), mask) + codes = npy.take( + self._masked_array_to_path_code_mapping, + mdif) + self._path = Path(self._xy, codes, closed=False) + # MGDTODO: If _draw_steps is removed, remove the following line also + self._step_path = None - if self._linestyle == 'steps': - siz=len(xt) - if siz<2: return - xt, yt = self._x, self._y - xt2=npy.ones((2*siz,), xt.dtype) - xt2[0:-1:2], xt2[1:-1:2], xt2[-1] = xt, xt[1:], xt[-1] - yt2=npy.ones((2*siz,), yt.dtype) - yt2[0:-1:2], yt2[1::2] = yt, yt - self._path = Path(npy.vstack((xt2, yt2)).transpose(), closed=False) - else: - self._path = Path(self._xy, closed=False) - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 @@ -507,14 +454,7 @@ funcname = self._lineStyles.get(self._linestyle, '_draw_nothing') lineFunc = getattr(self, funcname) - - # MGDTODO: Deal with self._segments - if self._segments is not None: - for ii in self._segments: - lineFunc(renderer, gc, xt[ii[0]:ii[1]], yt[ii[0]:ii[1]]) - - else: - lineFunc(renderer, gc, self._path) + lineFunc(renderer, gc, self._path) # MGDTODO: Deal with markers if self._marker is not None: @@ -709,7 +649,29 @@ def _draw_nothing(self, renderer, gc, path): pass - + + def _draw_steps(self, renderer, gc, path): + # We generate the step function path on-the-fly, and then cache it. + # The cache may be later invalidated when the data changes + # (in self.recache()) + + # MGDTODO: Untested -- using pylab.step doesn't actually trigger + # this code -- the path is "stepped" before even getting to this + # class. Perhaps this should be removed here, since it is not as + # powerful as what is in axes.step() anyway. + if self._step_path is None: + vertices = self._path.vertices + codes = self._path.codes + siz = len(vertices) + if siz<2: return + new_vertices = npy.zeros((2*siz, 2), vertices.dtype) + new_vertices[0:-1:2, 0], new_vertices[1:-1:2, 0], newvertices[-1, 0] = vertices[:, 0], vertices[1:, 0], vertices[-1, 0] + new_vertices[0:-1:2, 1], new_vertices[1::2, 1] = vertices[:, 1], vertices[:, 1] + self._step_path = Path(new_vertices, closed=False) + gc.set_linestyle('solid') + renderer.draw_path(gc, self._step_path, self.get_transform()) + + def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/path.py 2007-09-19 16:18:51 UTC (rev 3858) @@ -1,10 +1,10 @@ import numpy as npy -VALIDATE_PATHS = True +DEBUG = True class Path(object): # Path codes - STOP = 0 + IGNORE = 0 # 1 vertex MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices @@ -18,7 +18,7 @@ UBSPLINE = 8 #### - NUM_VERTICES = [0, 1, 1, 2, 3, 0] + NUM_VERTICES = [1, 1, 1, 2, 3, 0] code_type = npy.uint8 @@ -43,7 +43,7 @@ assert self._codes.ndim == 1 - if VALIDATE_PATHS: + if DEBUG: i = 0 NUM_VERTICES = self.NUM_VERTICES for code in codes: Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 16:18:51 UTC (rev 3858) @@ -32,18 +32,6 @@ for child in children: getattr(self, child)._parents.add(self) self._children = children - - # MGDTODO: decide whether we need this in-place updating and - # remove if not -# def replace_child(self, index, child): -# children = self._children -# getattr(self, children[index])._parents.remove(self) -# setattr(self, children[index], child) -# # We have to reset children in case two or more -# # of the children are the same -# for child in children: -# getattr(self, child)._parents.add(self) -# self.invalidate() class BboxBase(TransformNode): ''' @@ -327,53 +315,7 @@ if self._points is None: self._points = self.transform(self.bbox.get_points()) return self._points - -# MGDTODO: This code probably works, but I don't think it's a good idea -# (from a code clarity perspective) -# class BlendedBbox(BboxBase): -# def __init__(self, bbox_x, bbox_y): -# assert isinstance(bbox_x, BboxBase) -# assert isinstance(bbox_y, BboxBase) - -# BboxBase.__init__(self) -# self._x = bbox_x -# self._y = bbox_y -# self.set_children(['_x', '_y']) -# self._points = None - -# def __repr__(self): -# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform) -# __str__ = __repr__ -# def _do_invalidation(self): -# self._points = None - -# def get_points(self): -# if self._points is None: -# # MGDTODO: Optimize -# if self._x == self._y: -# self._points = self._x.get_points() -# else: -# x_points = self._x.get_points() -# y_points = self._y.get_points() -# self._points = npy.array( -# [[x_points[0,0], y_points[0,1]], -# [x_points[1,0], y_points[1,1]]], -# npy.float_) -# return self._points - -# def _set_intervalx(self, pair): -# # MGDTODO: Optimize -# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]]) -# self.replace_child(0, bbox) -# intervalx = property(BboxBase._get_intervalx, _set_intervalx) - -# def _set_intervaly(self, pair): -# # MGDTODO: Optimize -# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]]) -# self.replace_child(1, bbox) -# intervaly = property(BboxBase._get_intervaly, _set_intervaly) - class Transform(TransformNode): def __init__(self): TransformNode.__init__(self) @@ -746,7 +688,6 @@ self._mtx = affine._mtx return self._mtx -# MGDTODO: There's probably a better place for this def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Ensure the endpoints of a range are not too close together. Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/src/_backend_agg.cpp 2007-09-19 16:18:51 UTC (rev 3858) @@ -634,6 +634,9 @@ if (num_vertices) { for (size_t j=0; j<num_vertices; ++j) GET_NEXT_VERTEX(x, y); + if (code_i == IGNORE) + continue; + trans.transform(&x, &y); if (face.first) { @@ -860,6 +863,10 @@ for (size_t i = 0; i < N; ++i) { switch (*(unsigned char*)(code_i)) { + case IGNORE: + GET_NEXT_VERTEX(x0, y0); + _VERBOSE("IGNORE"); + break; case MOVETO: GET_NEXT_VERTEX(x0, y0); move_to(x0, y0); Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-09-19 13:28:11 UTC (rev 3857) +++ branches/transforms/src/_backend_agg.h 2007-09-19 16:18:51 UTC (rev 3858) @@ -40,14 +40,14 @@ #include "agg_vcgen_markers_term.h" // These are copied directly from path.py, and must be kept in sync -#define STOP 0 +#define IGNORE 0 #define MOVETO 1 #define LINETO 2 #define CURVE3 3 #define CURVE4 4 #define CLOSEPOLY 5 -const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 }; +const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 }; typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |