You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(115) |
Aug
(120) |
Sep
(137) |
Oct
(170) |
Nov
(461) |
Dec
(263) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(120) |
Feb
(74) |
Mar
(35) |
Apr
(74) |
May
(245) |
Jun
(356) |
Jul
(240) |
Aug
(115) |
Sep
(78) |
Oct
(225) |
Nov
(98) |
Dec
(271) |
2009 |
Jan
(132) |
Feb
(84) |
Mar
(74) |
Apr
(56) |
May
(90) |
Jun
(79) |
Jul
(83) |
Aug
(296) |
Sep
(214) |
Oct
(76) |
Nov
(82) |
Dec
(66) |
2010 |
Jan
(46) |
Feb
(58) |
Mar
(51) |
Apr
(77) |
May
(58) |
Jun
(126) |
Jul
(128) |
Aug
(64) |
Sep
(50) |
Oct
(44) |
Nov
(48) |
Dec
(54) |
2011 |
Jan
(68) |
Feb
(52) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2018 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <md...@us...> - 2007-09-20 12:31:27
|
Revision: 3861 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3861&view=rev Author: mdboom Date: 2007-09-20 05:31:26 -0700 (Thu, 20 Sep 2007) Log Message: ----------- Fix font.size from being saved in the fontManager.cache Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/font_manager.py Modified: trunk/matplotlib/lib/matplotlib/font_manager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/font_manager.py 2007-09-19 19:48:17 UTC (rev 3860) +++ trunk/matplotlib/lib/matplotlib/font_manager.py 2007-09-20 12:31:26 UTC (rev 3861) @@ -773,6 +773,7 @@ else: if is_string_like(size): parent_size = fontManager.get_default_size() + print "parent_size", parent_size, size scaling = font_scalings.get(size) if scaling is not None: size = parent_size * scaling @@ -843,10 +844,9 @@ """ def __init__(self, size=None, weight='normal'): - if not size : size = rcParams['font.size'] - self.__default_size = size self.__default_weight = weight - + self.default_size = size + paths = [os.path.join(rcParams['datapath'],'fonts','ttf'), os.path.join(rcParams['datapath'],'fonts','afm')] @@ -899,7 +899,9 @@ def get_default_size(self): "Return the default font size." - return self.__default_size + if self.default_size is None: + return rcParams['font.size'] + return self.default_size def set_default_weight(self, weight): "Set the default font weight. The initial value is 'normal'." @@ -1085,6 +1087,7 @@ try: fontManager = pickle_load(_fmcache) + fontManager.default_size = None verbose.report("Using fontManager instance from %s" % _fmcache) except: _rebuild() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-19 19:48:22
|
Revision: 3860 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3860&view=rev Author: mdboom Date: 2007-09-19 12:48:17 -0700 (Wed, 19 Sep 2007) Log Message: ----------- Use iterator rather than caching approach for paths Modified Paths: -------------- branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-19 19:46:34 UTC (rev 3859) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-19 19:48:17 UTC (rev 3860) @@ -112,8 +112,7 @@ self.dpi = dpi self.width = width self.height = height - if __debug__: verbose.report('RendererAgg.__init__ width=%s, \ - height=%s'%(width, height), 'debug-annoying') + if __debug__: verbose.report('RendererAgg.__init__ width=%s, height=%s'%(width, height), 'debug-annoying') self._renderer = _RendererAgg(int(width), int(height), dpi, debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-19 19:46:34 UTC (rev 3859) +++ branches/transforms/src/_backend_agg.cpp 2007-09-19 19:48:17 UTC (rev 3860) @@ -87,15 +87,17 @@ inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, double& x, double& y, size_t next_vertex_stride, - size_t next_axis_stride) { + size_t next_axis_stride, + const char* & code_i, size_t code_stride) { if (vertex_i + next_axis_stride >= vertex_end) throw Py::ValueError("Error parsing path. Read past end of vertices"); x = *(double*)vertex_i; y = *(double*)(vertex_i + next_axis_stride); vertex_i += next_vertex_stride; + code_i += code_stride; } -#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) +#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride, code_i, code_stride) Py::Object BufferRegion::to_string(const Py::Tuple &args) { @@ -103,7 +105,71 @@ return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); } +class PathIterator { + PyArrayObject* vertices; + PyArrayObject* codes; + size_t m_iterator; + size_t m_total_vertices; +public: + PathIterator(const Py::Object& path_obj) : + vertices(NULL), codes(NULL), m_iterator(0) { + Py::Object vertices_obj = path_obj.getAttr("vertices"); + Py::Object codes_obj = path_obj.getAttr("codes"); + + vertices = (PyArrayObject*)PyArray_ContiguousFromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) + throw Py::ValueError("Invalid vertices array."); + codes = (PyArrayObject*)PyArray_ContiguousFromObject + (codes_obj.ptr(), PyArray_UINT8, 1, 1); + if (!codes) + throw Py::ValueError("Invalid codes array."); + + if (codes->dimensions[0] != vertices->dimensions[0]) + throw Py::ValueError("Vertices and codes array are not the same length."); + + m_total_vertices = codes->dimensions[0]; + } + + ~PathIterator() { + Py_XDECREF(vertices); + Py_XDECREF(codes); + } + + static const char code_map[]; + + inline unsigned vertex(unsigned idx, double* x, double* y) { + if (idx > m_total_vertices) + throw Py::RuntimeError("Requested vertex past end"); + double* pv = (double*)(vertices->data + (idx * vertices->strides[0])); + *x = *pv++; + *y = *pv; + // MGDTODO: Range check + return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))]; + } + + inline unsigned vertex(double* x, double* y) { + if(m_iterator >= m_total_vertices) return agg::path_cmd_stop; + return vertex(m_iterator++, x, y); + } + + inline void rewind(unsigned path_id) { + m_iterator = path_id; + } + + inline unsigned total_vertices() { + return m_total_vertices; + } +}; + +const char PathIterator::code_map[] = {0, + agg::path_cmd_move_to, + agg::path_cmd_line_to, + agg::path_cmd_curve3, + agg::path_cmd_curve4, + agg::path_cmd_end_poly | agg::path_flags_close}; + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), @@ -634,7 +700,7 @@ if (num_vertices) { for (size_t j=0; j<num_vertices; ++j) GET_NEXT_VERTEX(x, y); - if (code_i == IGNORE) + if (*code_i == STOP || *code_i == CLOSEPOLY) continue; trans.transform(&x, &y); @@ -863,9 +929,10 @@ for (size_t i = 0; i < N; ++i) { switch (*(unsigned char*)(code_i)) { - case IGNORE: + case STOP: GET_NEXT_VERTEX(x0, y0); - _VERBOSE("IGNORE"); + _VERBOSE("STOP"); + // MGDTODO: If this isn't the end, we should raise an error break; case MOVETO: GET_NEXT_VERTEX(x0, y0); @@ -894,10 +961,10 @@ break; case CLOSEPOLY: close_polygon(); + GET_NEXT_VERTEX(x0, y0); _VERBOSE("CLOSEPOLY"); break; } - code_i += code_stride; } } catch(...) { Py_XDECREF(vertices); @@ -911,7 +978,7 @@ Py::Object RendererAgg::draw_path(const Py::Tuple& args) { - typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_transform<PathIterator> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; @@ -928,9 +995,11 @@ GCAgg gc = GCAgg(args[0], dpi); Py::Object path_obj = args[1]; - if (!PathAgg::check(path_obj)) - throw Py::TypeError("Native path object is not of correct type"); - PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); +// if (!PathAgg::check(path_obj)) +// throw Py::TypeError("Native path object is not of correct type"); + // PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); + PathIterator path(path_obj); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]); facepair_t face = _get_rgba_face(args[3], gc.alpha); @@ -943,34 +1012,34 @@ bool has_clippath = (gc.clippath != NULL); if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) { - rendererBaseAlphaMask->clear(agg::gray8(0, 0)); - gc.clippath->rewind(0); - transformed_path_t transformed_clippath(*(gc.clippath), trans); - theRasterizer->add_path(transformed_clippath); - rendererAlphaMask->color(agg::gray8(255, 255)); - agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); - lastclippath = gc.clippath; - lastclippath_transform = trans; +// rendererBaseAlphaMask->clear(agg::gray8(0, 0)); +// gc.clippath->rewind(0); +// transformed_path_t transformed_clippath(*(gc.clippath), trans); +// theRasterizer->add_path(transformed_clippath); +// rendererAlphaMask->color(agg::gray8(255, 255)); +// agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); +// lastclippath = gc.clippath; +// lastclippath_transform = trans; } try { // If this is a straight horizontal or vertical line, quantize to nearest // pixels - if (path->total_vertices() == 2) { - double x0, y0, x1, y1; - path->vertex(0, &x0, &y0); - trans.transform(&x0, &y0); - path->vertex(1, &x1, &y1); - trans.transform(&x1, &y1); - if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { - new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); - new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); - tpath = new transformed_path_t(new_path, agg::trans_affine()); - } - } +// if (path.total_vertices() == 2) { +// double x0, y0, x1, y1; +// path.vertex(0, &x0, &y0); +// trans.transform(&x0, &y0); +// path.vertex(1, &x1, &y1); +// trans.transform(&x1, &y1); +// if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { +// new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); +// new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); +// tpath = new transformed_path_t(new_path, agg::trans_affine()); +// } +// } if (!tpath) { - tpath = new transformed_path_t(*path, trans); + tpath = new transformed_path_t(path, trans); } // Benchmarking shows that there is no noticable slowdown to always Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-09-19 19:46:34 UTC (rev 3859) +++ branches/transforms/src/_backend_agg.h 2007-09-19 19:48:17 UTC (rev 3860) @@ -40,14 +40,14 @@ #include "agg_vcgen_markers_term.h" // These are copied directly from path.py, and must be kept in sync -#define IGNORE 0 +#define STOP 0 #define MOVETO 1 #define LINETO 2 #define CURVE3 3 #define CURVE4 4 #define CLOSEPOLY 5 -const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 }; +const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 }; 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. |
From: <md...@us...> - 2007-09-19 19:46:41
|
Revision: 3859 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3859&view=rev Author: mdboom Date: 2007-09-19 12:46:34 -0700 (Wed, 19 Sep 2007) Log Message: ----------- Lots of minor fixes Modified Paths: -------------- branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/pbox.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -252,12 +252,9 @@ if not is_numlike(self.pickradius): raise ValueError,"pick radius should be a distance" - if self._newstyle: - # transform in backend - x = self._x - y = self._y - else: - x, y = self._get_plottable() + # transform in backend + x = self._x + y = self._y if len(x)==0: return False,{} xt, yt = self.get_transform().numerix_x_y(x, y) @@ -337,7 +334,6 @@ ACCEPTS: (npy.array xdata, npy.array ydata) """ - if len(args)==1: x, y = args[0] else: @@ -347,8 +343,9 @@ self._yorig = y self.recache() + # MGDTODO: Masked data arrays are broken _masked_array_to_path_code_mapping = npy.array( - [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type) + [Path.LINETO, Path.MOVETO, 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 @@ -387,18 +384,18 @@ # MGDTODO: If _draw_steps is removed, remove the following line also self._step_path = None - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 return npy.alltrue(x[1:]-x[0:-1]>=0) + # MGDTODO: Remove me (seems to be used for old-style interface only) def _get_plottable(self): # If log scale is set, only pos data will be returned x, y = self._x, self._y - # MGDTODO: Deal with the log scale here + # MGDTODO: (log-scaling) # try: logx = self.get_transform().get_funcx().get_type()==LOG10 # except RuntimeError: logx = False # non-separable Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -319,8 +319,6 @@ return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) - # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will - # happen automatically (without needing to call set_x etc. def __init__(self, xy, width, height, **kwargs): """ xy is an x,y tuple lower, left @@ -459,17 +457,14 @@ def __init__(self, xy, **kwargs): """ - xy is a sequence of (x,y) 2 tuples + xy is a numpy array with shape Nx2 Valid kwargs are: %(Patch)s See Patch documentation for additional kwargs """ - # MGDTODO: This should encourage the use of numpy arrays of shape Nx2 Patch.__init__(self, **kwargs) - if not isinstance(xy, list): - xy = list(xy) - self._path = Path(xy, closed=False) + self._path = Path(xy, closed=True) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_verts(self): Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/path.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -1,15 +1,13 @@ import numpy as npy -DEBUG = True - class Path(object): # Path codes - IGNORE = 0 # 1 vertex + STOP = 0 # 1 vertex MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices - CLOSEPOLY = 5 + CLOSEPOLY = 5 # 1 vertex ### # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, # so if they don't, we probably shouldn't @@ -18,38 +16,36 @@ UBSPLINE = 8 #### - NUM_VERTICES = [1, 1, 1, 2, 3, 0] + NUM_VERTICES = [1, 1, 1, 2, 3, 1] code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): - self._vertices = npy.asarray(vertices, npy.float_) - assert self._vertices.ndim == 2 - assert self._vertices.shape[1] == 2 - + vertices = npy.asarray(vertices, npy.float_) + assert vertices.ndim == 2 + assert vertices.shape[1] == 2 + if codes is None: if closed: codes = self.LINETO * npy.ones( - self._vertices.shape[0] + 1, self.code_type) + vertices.shape[0] + 1, self.code_type) codes[0] = self.MOVETO - codes[-1] = self.CLOSEPOLY + codes[-1] = self.CLOSEPOLY + vertices = npy.concatenate((vertices, [[0.0, 0.0]])) else: codes = self.LINETO * npy.ones( - self._vertices.shape[0], self.code_type) + vertices.shape[0], self.code_type) codes[0] = self.MOVETO else: codes = npy.asarray(codes, self.code_type) - self._codes = codes - + assert codes.ndim == 1 + assert len(codes) == len(vertices) + + self._codes = codes + self._vertices = vertices + assert self._codes.ndim == 1 - if DEBUG: - i = 0 - NUM_VERTICES = self.NUM_VERTICES - for code in codes: - i += NUM_VERTICES[code] - assert i == len(self.vertices) - def __repr__(self): return "Path(%s, %s)" % (self.vertices, self.codes) @@ -66,11 +62,13 @@ NUM_VERTICES = self.NUM_VERTICES vertices = self.vertices for code in self.codes: - num_vertices = NUM_VERTICES[code] - if num_vertices >= 1: - i += num_vertices - 1 - yield vertices[i] - i += 1 + if code == self.CLOSEPOLY: + i += 1 + else: + num_vertices = NUM_VERTICES[code] + i += num_vertices - 1 + yield vertices[i] + i += 1 _unit_rectangle = None #@classmethod @@ -118,16 +116,18 @@ [-offset, -1.0], [-1.0, -offset], - [-1.0, 0.0]], - npy.float_) - codes = npy.array( - [cls.MOVETO, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CLOSEPOLY], - cls.code_type) + [-1.0, 0.0], + + [-1.0, 0.0]], + npy.float_) + + codes = cls.CURVE4 + npy.ones((len(vertices))) + codes[0] = cls.MOVETO + codes[-1] = cls.CLOSEPOLY + cls._unit_circle = Path(vertices, codes) return cls._unit_circle unit_circle = classmethod(unit_circle) + +# MGDTODO: Add a transformed path that would automatically invalidate +# itself when its transform changes Modified: branches/transforms/lib/matplotlib/pbox.py =================================================================== --- branches/transforms/lib/matplotlib/pbox.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/pbox.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -1,5 +1,3 @@ -# MGDTODO: Just included verbatim for now - class PBox(list): ''' A left-bottom-width-height (lbwh) specification of a bounding box, Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/text.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -231,7 +231,7 @@ # now rotate the bbox - cornersRotated = M(cornersHoriz) + cornersRotated = M.transform(cornersHoriz) txs = cornersRotated[:, 0] tys = cornersRotated[:, 1] @@ -269,7 +269,7 @@ # now rotate the positions around the first x,y position - xys = M(offsetLayout) + xys = M.transform(offsetLayout) tx = xys[:, 0] ty = xys[:, 1] tx += offsetx @@ -277,7 +277,7 @@ # now inverse transform back to data coords inverse_transform = self.get_transform().inverted() - xys = inverse_transform(xys) + xys = inverse_transform.transform(xys) xs, ys = zip(*xys) @@ -407,7 +407,7 @@ return (x, y, self._text, self._color, self._verticalalignment, self._horizontalalignment, hash(self._fontproperties), self._rotation, - self.get_transform().to_values(), + self.get_transform(), ) def get_text(self): Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -32,7 +32,8 @@ for child in children: getattr(self, child)._parents.add(self) self._children = children - + + class BboxBase(TransformNode): ''' This is the read-only part of a bounding-box @@ -293,6 +294,7 @@ return Bbox.from_lbrt(xmin, ymin, xmax, ymax) union = staticmethod(union) + class TransformedBbox(BboxBase): def __init__(self, bbox, transform): assert isinstance(bbox, Bbox) @@ -313,16 +315,20 @@ def get_points(self): if self._points is None: - self._points = self.transform(self.bbox.get_points()) + self._points = self.transform.transform(self.bbox.get_points()) return self._points + class Transform(TransformNode): def __init__(self): TransformNode.__init__(self) - def __call__(self, points): + def transform(self, points): raise NotImplementedError() + def transform_without_affine(self, points): + return self.transform(points), IDENTITY + def __add__(self, other): if isinstance(other, Transform): return composite_transform_factory(self, other) @@ -336,7 +342,7 @@ "Can not add Transform to object of type '%s'" % type(other)) def transform_point(self, point): - return self.__call__(npy.asarray([point]))[0] + return self.transform(npy.asarray([point]))[0] def has_inverse(self): raise NotImplementedError() @@ -350,6 +356,7 @@ def is_affine(self): return False + class Affine2DBase(Transform): input_dims = 2 output_dims = 2 @@ -390,7 +397,7 @@ def get_matrix(self): raise NotImplementedError() - def __call__(self, points): + def transform(self, points): """ Applies the transformation to an array of 2D points and returns the result. @@ -414,6 +421,11 @@ points = npy.dot(mtx[0:2, 0:2], points) points = points + mtx[0:2, 2:] return points.transpose() + + def transform_without_affine(self, points): + # MGDTODO: Should we copy the points here? I'd like to avoid it, + # if possible + return points, self def inverted(self): if self._inverted is None: @@ -430,9 +442,6 @@ class Affine2D(Affine2DBase): - input_dims = 2 - output_dims = 2 - def __init__(self, matrix = None): """ Initialize an Affine transform from a 3x3 numpy float array. @@ -535,40 +544,82 @@ def is_affine(self): return True + +IDENTITY = Affine2D() class BlendedGenericTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): # Here we ask: "Does it blend?" assert x_transform.is_separable() assert y_transform.is_separable() - + assert x_transform.input_dims == x_transform.output_dims == 2 + assert y_transform.input_dims == y_transform.output_dims == 2 + Transform.__init__(self) self._x = x_transform self._y = y_transform self.set_children(['_x', '_y']) - def __call__(self, points): - if self._x == self._y: + def transform(self, points): + # MGDTODO: Optimize the case where one of these is + # an affine + x = self._x + y = self._y + if x == y and x.input_dims == 2: return self._x(points) - - x_points = self._x(points) - y_points = self._y(points) - # This works because we already know the transforms are - # separable - return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) -# def set_x_transform(self, x_transform): -# self.replace_child(0, x_transform) + if x.input_dims == 2: + x_points = x.transform(points)[:, 0] + else: + x_points = x.transform(points[:, 0]) -# def set_y_transform(self, y_transform): -# self.replace_child(1, y_transform) + if y.input_dims == 2: + y_points = y.transform(points)[:, 1] + else: + y_points = y.transform(points[:, 1]) + return npy.vstack((x_points, y_points)).transpose() + + def inverted(self): + return BlendedGenericTransform(self._x.inverted(), self._y.inverted()) -class BlendedAffine2D(Affine2DBase, BlendedGenericTransform): + def is_separable(self): + return True + + +class BlendedSeparableTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): + # Here we ask: "Does it blend?" + assert x_transform.is_separable() + assert y_transform.is_separable() + assert x_transform.input_dims == x.transform.output_dims == 1 + assert y_transform.input_dims == y.transform.output_dims == 1 + + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) + + def transform(self, points): + x_points = self._x(points[:, 0]) + y_points = self._y(points[:, 1]) + return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose() + + +class BlendedAffine2D(Affine2DBase, Transform): + def __init__(self, x_transform, y_transform): assert x_transform.is_affine() assert y_transform.is_affine() - BlendedGenericTransform.__init__(self, x_transform, y_transform) + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) Affine2DBase.__init__(self) self._mtx = None @@ -597,12 +648,14 @@ # c to zero. self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) return self._mtx - + + def blended_transform_factory(x_transform, y_transform): if x_transform.is_affine() and y_transform.is_affine(): return BlendedAffine2D(x_transform, y_transform) return BlendedGenericTransform(x_transform, y_transform) + class CompositeGenericTransform(Transform): def __init__(self, a, b): assert a.output_dims == b.input_dims @@ -614,9 +667,17 @@ self._b = b self.set_children(['_a', '_b']) - def __call__(self, points): - return self._b(self._a(points)) + def transform(self, points): + return self._b.transform(self._a.transform(points)) + + def inverted(self): + return CompositeGenericTransform(self._b.inverted(), self._a.inverted()) + def is_separable(self): + return True + return self._a.is_separable() and self._b.is_separable() + + class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): assert a.is_affine() @@ -643,11 +704,32 @@ self._b.get_matrix()) return self._mtx + def composite_transform_factory(a, b): if a.is_affine() and b.is_affine(): return CompositeAffine2D(a, b) return CompositeGenericTransform(a, b) + + +class LogTransform(Transform): + input_dims = 1 + output_dims = 1 + def transform(self, a): + m = npy.ma.masked_where(a < 0, a) + return npy.log10(m) + + +class TestLogTransform(Transform): + input_dims = 2 + output_dims = 2 + def transform(self, xy): + return xy * 2 + + def inverted(self): + return self + + class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): assert isinstance(boxin, BboxBase) @@ -688,6 +770,7 @@ self._mtx = affine._mtx return self._mtx + def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Ensure the endpoints of a range are not too close together. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
From: <md...@us...> - 2007-09-19 13:28:21
|
Revision: 3857 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3857&view=rev Author: mdboom Date: 2007-09-19 06:28:11 -0700 (Wed, 19 Sep 2007) Log Message: ----------- Got legend working with new transforms Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-18 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-19 13:28:11 UTC (rev 3857) @@ -632,6 +632,7 @@ self.transAxes = mtransforms.BboxTransform( mtransforms.Bbox.unit(), self.bbox) + # self.set_transform(self.transAxes) self.transData = mtransforms.BboxTransform( self.viewLim, self.bbox) @@ -724,6 +725,7 @@ self.axesPatch.set_figure(self.figure) self.axesPatch.set_transform(self.transAxes) self.axesPatch.set_linewidth(rcParams['axes.linewidth']) + # MGDTODO: What is axesFrame for? We already have axesPatch self.axesFrame = mlines.Line2D((0,1,1,0,0), (0,0,1,1,0), linewidth=rcParams['axes.linewidth'], color=rcParams['axes.edgecolor'], @@ -5201,7 +5203,7 @@ Subplot(211) # 2 rows, 1 column, first (upper) plot """ def __str__(self): - return "Subplot(%g,%g)"%(self.bottom.get(),self.left.get()) + return "Subplot(%f,%f,%f,%f)" % (self.bbox.bounds) def __init__(self, fig, *args, **kwargs): """ Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-09-18 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/legend.py 2007-09-19 13:28:11 UTC (rev 3857) @@ -164,7 +164,7 @@ else: raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent - self.set_transform( BboxTransform( Bbox.unit(), parent.bbox) ) + self.set_transform( BboxTransform(Bbox.unit(), parent.bbox) ) if loc is None: loc = rcParams["legend.loc"] @@ -223,7 +223,7 @@ a.set_transform(self.get_transform()) def _approx_text_height(self): - return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height() + return self.fontsize/72.0*self.figure.dpi/self.parent.bbox.height def draw(self, renderer): @@ -531,7 +531,7 @@ def get_tbounds(text): #get text bounds in axes coords bbox = text.get_window_extent(renderer) bboxa = bbox.inverse_transformed(self.get_transform()) - return bboxa.get_bounds() + return bboxa.bounds hpos = [] for t, tabove in zip(self.texts[1:], self.texts[:-1]): @@ -560,10 +560,10 @@ # Set the data for the legend patch bbox = copy.copy(self._get_handle_text_bbox(renderer)) - bbox = bbox.scaled(1 + self.pad, 1 + self.pad) - l,b,w,h = bbox.get_bounds() - self.legendPatch.set_bounds(l,b,w,h) - + bbox = bbox.expanded(1 + self.pad, 1 + self.pad) + l, b, w, h = bbox.bounds + self.legendPatch.set_bounds(l, b, w, h) + ox, oy = 0, 0 # center if iterable(self._loc) and len(self._loc)==2: Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-18 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 13:28:11 UTC (rev 3857) @@ -117,7 +117,7 @@ '--' : '_draw_dashed', '-.' : '_draw_dash_dot', ':' : '_draw_dotted', - 'steps': '_draw_steps', + 'steps': '_draw_solid', 'None' : '_draw_nothing', ' ' : '_draw_nothing', '' : '_draw_nothing', @@ -352,10 +352,10 @@ self._picker = p def get_window_extent(self, renderer): - xys = self.get_transform()(self._xys) + xy = self.get_transform()(self._xy) - x = xys[:, 0] - y = xys[:, 1] + x = xy[:, 0] + y = xy[:, 1] left = x.min() bottom = y.min() width = x.max() - left @@ -426,9 +426,19 @@ npy.asarray(y, npy.float_))).transpose() self._x = self._xy[:, 0] self._y = self._xy[:, 1] - self._path = Path(self._xy, closed=False) - self._logcache = 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): @@ -700,24 +710,6 @@ pass - def _draw_steps(self, renderer, gc, xt, yt): - # MGDTODO: This is a quirky one. The step-plotting part - # should probably be moved to where the path is generated - # in recache, and then just drawn with _draw_solid - siz=len(xt) - if siz<2: return - 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 - gc.set_linestyle('solid') - - if self._newstyle: - renderer.draw_lines(gc, xt2, yt2, self.get_transform()) - else: - renderer.draw_lines(gc, xt2, yt2) - - def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-18 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-19 13:28:11 UTC (rev 3857) @@ -212,8 +212,8 @@ gc.set_hatch(self._hatch ) path = self.get_path() - transform = self.get_transform() - + transform = self.get_patch_transform() + self.get_transform() + renderer.draw_path(gc, path, transform, rgbFace) #renderer.close_group('patch') @@ -284,7 +284,7 @@ self.patch = patch self.props = props self.ox, self.oy = ox, oy - self._shadow_transform = transforms.Affine2D.translate(self.ox, self.oy) + self._shadow_transform = transforms.Affine2D().translate(self.ox, self.oy) self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -305,8 +305,8 @@ def get_path(self): return self.patch.get_path() - def get_transform(self): - return self._transform + self._shadow_transform + def get_patch_transform(self): + return self._shadow_transform class Rectangle(Patch): """ @@ -315,8 +315,6 @@ """ - _path = Path.unit_rectangle() - def __str__(self): return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) @@ -346,16 +344,14 @@ """ Return the vertices of the rectangle """ - # This is a "class-static" variable, so all rectangles in the plot - # will be shared (and merely have different transforms) - return self._path + return Path.unit_rectangle() # MGDTODO: Convert units # left, right = self.convert_xunits((x, x + self.width)) # bottom, top = self.convert_yunits((y, y + self.height)) - def get_transform(self): - return self._rect_transform + self._transform + def get_patch_transform(self): + return self._rect_transform def get_x(self): "Return the left coord of the rectangle" @@ -379,7 +375,8 @@ ACCEPTS: float """ - self._bbox.xmin = x + w = self._bbox.width + self._bbox.intervalx = (x, x + w) def set_y(self, y): """ @@ -387,7 +384,8 @@ ACCEPTS: float """ - self._bbox.ymin = y + h = self._bbox.height + self._bbox.intervaly = (y, y + h) def set_width(self, w): """ @@ -395,7 +393,7 @@ ACCEPTS: float """ - self._bbox.width = w + self._bbox.xmax = self._bbox.xmin + w def set_height(self, h): """ @@ -403,7 +401,7 @@ ACCEPTS: float """ - self._bbox.height = h + self._bbox.ymax = self._bbox.ymin + h def set_bounds(self, *args): """ Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-18 19:29:21 UTC (rev 3856) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 13:28:11 UTC (rev 3857) @@ -8,6 +8,8 @@ from numpy.linalg import inv from sets import Set +DEBUG = True + # MGDTODO: This creates a ton of cyclical references. We may want to # consider using weak references @@ -53,6 +55,13 @@ def __array__(self): return self.get_points() + + if DEBUG: + def invalidate(self): + points = self.get_points() + assert points[0, 0] <= points[1, 0] + assert points[0, 1] <= points[1, 1] + TransformNode.invalidate(self) # MGDTODO: Probably a more efficient ways to do this... def _get_xmin(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-18 19:29:24
|
Revision: 3856 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3856&view=rev Author: mdboom Date: 2007-09-18 12:29:21 -0700 (Tue, 18 Sep 2007) Log Message: ----------- Optimize shared axes (to prevent calling set_xlim/set_ylim more than once per axes per update). Save figure at correct dpi. General cleanup and optimizations. Modified Paths: -------------- 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_agg.py branches/transforms/lib/matplotlib/backends/backend_gtkagg.py branches/transforms/lib/matplotlib/backends/backend_tkagg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -435,6 +435,9 @@ 1 : 'log', } + _shared_x_axes = cbook.Grouper() + _shared_y_axes = cbook.Grouper() + def __str__(self): return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds) def __init__(self, fig, rect, @@ -491,6 +494,10 @@ # must be set before set_figure self._sharex = sharex self._sharey = sharey + if sharex is not None: + self._shared_x_axes.join(self, sharex) + if sharey is not None: + self._shared_y_axes.join(self, sharey) # Flag: True if some other Axes instance is sharing our x or y axis self._masterx = False self._mastery = False @@ -502,7 +509,6 @@ # this call may differ for non-sep axes, eg polar self._init_axis() - if axisbg is None: axisbg = rcParams['axes.facecolor'] self._axisbg = axisbg self._frameon = frameon @@ -616,55 +622,28 @@ #these will be updated later as data is added self._set_lim_and_transforms() - def _shared_xlim_callback(self, ax): - xmin, xmax = ax.get_xlim() - self.set_xlim(xmin, xmax, emit=False) - self.figure.canvas.draw_idle() - - def _shared_ylim_callback(self, ax): - ymin, ymax = ax.get_ylim() - self.set_ylim(ymin, ymax, emit=False) - self.figure.canvas.draw_idle() - def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ - Bbox = mtransforms.Bbox - self.viewLim = Bbox.unit() + self.viewLim = mtransforms.Bbox.unit() + self.dataLim = mtransforms.Bbox.unit() - if self._sharex is not None: - # MGDTODO: This may be doing at least one too many updates - # than necessary - self._sharex.callbacks.connect( - 'xlim_changed', self._shared_xlim_callback) - self.viewLim.intervalx = self._sharex.viewLim.intervalx - if self._sharey is not None: - self._sharey.callbacks.connect( - 'ylim_changed', self._shared_ylim_callback) - self.viewLim.intervaly = self._sharex.viewLim.intervaly - - self.dataLim = Bbox.unit() - self.transAxes = mtransforms.BboxTransform( - Bbox.unit(), self.bbox) - + mtransforms.Bbox.unit(), self.bbox) self.transData = mtransforms.BboxTransform( self.viewLim, self.bbox) def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' - # MGDTODO: This changed from returning a list to returning a Bbox - # If you get any errors with the result of this function, please - # update the calling code if original: - return copy.copy(self._originalPosition) + return self._originalPosition else: - return copy.copy(self._position) - # return [val.get() for val in self._position] + return self._position + def set_position(self, pos, which='both'): """ Set the axes position with pos = [left, bottom, width, height] @@ -699,8 +678,7 @@ self.xaxis.cla() self.yaxis.cla() - # MGDTODO - # self.dataLim.ignore(1) + self.ignore_existing_data_limits = True self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed')) if self._sharex is not None: @@ -886,7 +864,7 @@ return - l,b,w,h = self.get_position(original=True) + l,b,w,h = self.get_position(original=True).bounds box_aspect = fig_aspect * (h/w) data_ratio = box_aspect / A @@ -1152,7 +1130,7 @@ # Otherwise, it will compute the bounds of it's current data # and the data in xydata xys = npy.asarray(xys) - self.dataLim.update_numerix_xy(xys, -1) + self.update_datalim_numerix(xys[:, 0], xys[:, 1]) def update_datalim_numerix(self, x, y): @@ -1161,10 +1139,9 @@ # 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 - #print type(x), type(y) - # MGDTODO ## self.dataLim.update_numerix(x, y, -1) - self.dataLim.update_from_data(x, y) + self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits) + self.ignore_existing_data_limits = False def _get_verts_in_data_coords(self, trans, xys): if trans == self.transData: @@ -1264,9 +1241,7 @@ if not self.get_visible(): return renderer.open_group('axes') self.apply_aspect() - # MGDTODO -- this is where we can finalize all of the transforms - # self.transData.freeze() # eval the lazy objects - # self.transAxes.freeze() + if self.axison and self._frameon: self.axesPatch.draw(renderer) artists = [] @@ -1314,17 +1289,13 @@ if self.axison and self._frameon: artists.append(self.axesFrame) - # keep track of i to guarantee stable sort for python 2.2 - dsu = [ (a.zorder, i, a) for i, a in enumerate(artists) - if not a.get_animated()] + dsu = [ (a.zorder, a) for a in artists + if not a.get_animated() ] dsu.sort() - for zorder, i, a in dsu: + for zorder, a in dsu: a.draw(renderer) - # MGDTODO - # self.transData.thaw() # release the lazy objects - # self.transAxes.thaw() # release the lazy objects renderer.close_group('axes') self._cachedRenderer = renderer @@ -1509,7 +1480,6 @@ ACCEPTS: len(2) sequence of floats """ - if xmax is None and iterable(xmin): xmin,xmax = xmin @@ -1534,11 +1504,10 @@ self.viewLim.intervalx = (xmin, xmax) if emit: self.callbacks.process('xlim_changed', self) - # MGDTODO: It would be nice to do this is in the above callback list, - # but it's difficult to tell how to initialize this at the - # right time - if self._sharex: - self._sharex.set_xlim(*self.viewLim.intervalx) + # Call all of the other x-axes that are shared with this one + for other in self._shared_x_axes.get_siblings(self): + if other is not self: + other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False) return xmin, xmax @@ -1665,11 +1634,10 @@ self.viewLim.intervaly = (ymin, ymax) if emit: self.callbacks.process('ylim_changed', self) - # MGDTODO: It would be nice to do this is in the above callback list, - # but it's difficult to tell how to initialize this at the - # right time - if self._sharey: - self._sharey.set_ylim(*self.viewLim.intervaly) + # Call all of the other y-axes that are shared with this one + for other in self._shared_y_axes.get_siblings(self): + if other is not self: + other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False) return ymin, ymax Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/axis.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -1033,8 +1033,7 @@ bbox = Bbox.union(bboxes) bottom = bbox.ymin - self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi/72.0)) -# self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0)) MGDTODO + self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0)) else: if not len(bboxes2): @@ -1057,7 +1056,6 @@ bbox = Bbox.union(bboxes) bottom = bbox.ymin self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0)) -# self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO def set_ticks_position(self, position): """ @@ -1225,7 +1223,6 @@ left = bbox.xmin self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y)) - # self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y)) MGDTODO else: if not len(bboxes2): @@ -1245,7 +1242,6 @@ x,y = self.offsetText.get_position() top = self.axes.bbox.ymax self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0)) -# self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO def set_offset_position(self, position): assert position == 'left' or position == 'right' Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -18,8 +18,9 @@ """An abstract base class to handle drawing/rendering operations """ # This will cache paths across rendering instances - # Each subclass of RenderBase should define this --> - # _paths = weakref.WeakKeyDictionary() + # Each subclass of RenderBase should define this a weak-keyed + # dictionary to hold native paths + # _native_paths = weakref.WeakKeyDictionary() def __init__(self): self._texmanager = None @@ -44,7 +45,7 @@ self._native_paths[path] = native_path return native_path - def draw_path(self, gc, path, transform, rgbFace = None): + def draw_path(self, gc, path, transform, rgbFace=None): """ Handles the caching of the native path associated with the given path and calls the underlying backend's _draw_path to @@ -67,17 +68,31 @@ passed to them in draw_path. """ return path + + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + native_marker_path = self._get_cached_native_path(marker_path) + self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) - def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, - rotation): + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): """ - Draw an arc using GraphicsContext instance gcEdge, centered at x,y, - with width and height and angles from 0.0 to 360.0 - 0 degrees is at 3-o'clock - positive angles are anti-clockwise - draw rotated 'rotation' degrees anti-clockwise about x,y + This method is currently underscore hidden because the + draw_markers method is being used as a sentinel for newstyle + backend drawing - If the color rgbFace is not None, fill the arc with it. + path - a matplotlib.agg.path_storage instance + + Draw the marker specified in path with graphics context gc at + each of the locations in arrays x and y. trans is a + matplotlib.transforms.Transformation instance used to + transform x and y to display coords. It consists of an + optional nonlinear component and an affine. You can access + these two components as + + if transform.need_nonlinear(): + x,y = transform.nonlinear_only_numerix(x, y) + # the a,b,c,d,tx,ty affine which transforms x and y + vec6 = transform.as_vec6_val() + ...backend dependent affine... """ raise NotImplementedError @@ -109,30 +124,21 @@ """ return False - def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - native_marker_path = self._get_cached_native_path(marker_path) - self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) - - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): + ###################################################################### + ## OLD API IS BELOW + ## These functions no longer need to be implemented in the backends -- + ## they now perform all of their functions in terms of the new API. + + def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, + rotation): """ - This method is currently underscore hidden because the - draw_markers method is being used as a sentinel for newstyle - backend drawing + Draw an arc using GraphicsContext instance gcEdge, centered at x,y, + with width and height and angles from 0.0 to 360.0 + 0 degrees is at 3-o'clock + positive angles are anti-clockwise + draw rotated 'rotation' degrees anti-clockwise about x,y - path - a matplotlib.agg.path_storage instance - - Draw the marker specified in path with graphics context gc at - each of the locations in arrays x and y. trans is a - matplotlib.transforms.Transformation instance used to - transform x and y to display coords. It consists of an - optional nonlinear component and an affine. You can access - these two components as - - if transform.need_nonlinear(): - x,y = transform.nonlinear_only_numerix(x, y) - # the a,b,c,d,tx,ty affine which transforms x and y - vec6 = transform.as_vec6_val() - ...backend dependent affine... + If the color rgbFace is not None, fill the arc with it. """ raise NotImplementedError @@ -346,8 +352,10 @@ If rgbFace is not None, fill the rectangle with it. """ - raise NotImplementedError - + warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning) + transform = transforms.Affine2D().scale(width, height).translate(x, y) + self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace) + def draw_regpoly_collection( self, clipbox, offsets, transOffset, verts, sizes, facecolors, edgecolors, linewidths, antialiaseds): @@ -1221,8 +1229,6 @@ origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() - # MGDTODO - # self.figure.dpi.set(dpi) self.figure.dpi = dpi self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) @@ -1236,12 +1242,12 @@ orientation=orientation, **kwargs) finally: - # MGDTODO - # self.figure.dpi.set(origDPI) self.figure.dpi = origDPI self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) self.figure.set_canvas(self) + + self.draw() return result @@ -1623,8 +1629,8 @@ lims.append( (xmin, xmax, ymin, ymax) ) # Store both the original and modified positions pos.append( ( - a.get_position(True), - a.get_position() ) ) + copy.copy(a.get_position(True)), + copy.copy(a.get_position() )) ) self._views.push(lims) self._positions.push(pos) self.set_history_buttons() Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -114,9 +114,6 @@ self.height = height if __debug__: verbose.report('RendererAgg.__init__ width=%s, \ height=%s'%(width, height), 'debug-annoying') - # MGDTODO -# self._renderer = _RendererAgg(int(width), int(height), dpi.get(), -# debug=False) self._renderer = _RendererAgg(int(width), int(height), dpi, debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', @@ -136,35 +133,6 @@ def _draw_native_path(self, gc, path, transform, rgbFace): return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace) - def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): - """ - Draw an arc centered at x,y with width and height and angles - from 0.0 to 360.0 - - If rgbFace is not None, fill the rectangle with that color. gcEdge - is a GraphicsContext instance - - Currently, I'm only supporting ellipses, ie angle args are - ignored - """ - if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying') - self._renderer.draw_ellipse( - gcEdge, rgbFace, x, y, width/2, height/2, rotation) # ellipse takes radius - - - def draw_line(self, gc, x1, y1, x2, y2): - """ - x and y are equal length arrays, draw lines connecting each - point in x, y - """ - if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying') - x = npy.array([x1,x2], float) - y = npy.array([y1,y2], float) - self._renderer.draw_lines(gc, x, y) - - def draw_lines(self, gc, x, y, transform): - return self._renderer.draw_lines(gc, x, y, transform.to_values()) - def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): return self._renderer.draw_markers( gc, @@ -172,18 +140,6 @@ path.vertices, path.codes, trans.get_matrix(), rgbFace) - def draw_polygon(self, *args): - return self._renderer.draw_polygon(*args) - - def draw_point(self, gc, x, y): - """ - Draw a single point at x,y - """ - if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying') - rgbFace = gc.get_rgb() - self._renderer.draw_ellipse( - gc, rgbFace, x, y, 0.5, 0.5, 0.0) - def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext @@ -192,8 +148,6 @@ 'debug-annoying') ox, oy, width, height, descent, font_image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) -# ox, oy, width, height, descent, font_image, used_characters = \ -# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO x = int(x) + ox y = int(y) - oy @@ -227,7 +181,6 @@ # (in vector space) in the above call to font.set_text. self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, angle, gc) - def get_text_width_height_descent(self, s, prop, ismath, rgb=(0,0,0)): """ get the width and height in display coords of the string s @@ -241,7 +194,7 @@ # todo: handle props size = prop.get_size_in_points() texmanager = self.get_texmanager() - Z = texmanager.get_rgba(s, size, self.dpi.get(), rgb) + Z = texmanager.get_rgba(s, size, self.dpi, rgb) m,n,tmp = Z.shape # TODO: descent of TeX text (I am imitating backend_ps here -JKS) return n, m, 0 @@ -249,8 +202,6 @@ if ismath: ox, oy, width, height, descent, fonts, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) -# ox, oy, width, height, descent, fonts, used_characters = \ -# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO return width, height, descent font = self._get_agg_font(prop) font.set_text(s, 0.0, flags=LOAD_DEFAULT) # the width and height of unrotated string @@ -265,7 +216,7 @@ # todo, handle props, angle, origins rgb = gc.get_rgb() size = prop.get_size_in_points() - dpi = self.dpi.get() + dpi = self.dpi flip = angle==90 w,h,d = self.get_text_width_height_descent(s, prop, 'TeX', rgb) @@ -306,7 +257,6 @@ 'return the canvas width and height in display coords' return self.width, self.height - def _get_agg_font(self, prop): """ Get the font for text instance t, cacheing for efficiency @@ -325,11 +275,9 @@ font.clear() size = prop.get_size_in_points() font.set_size(size, self.dpi) - # font.set_size(size, self.dpi.get()) MGDTODO return font - def points_to_pixels(self, points): """ convert point measures to pixes using dpi and the pixels per @@ -337,8 +285,6 @@ """ if __debug__: verbose.report('RendererAgg.points_to_pixels', 'debug-annoying') - # MGDTODO - # return points*self.dpi.get()/72.0 return points*self.dpi/72.0 def tostring_rgb(self): @@ -404,9 +350,7 @@ self.figure.draw(self.renderer) def get_renderer(self): - l,b,w,h = self.figure.bbox.bounds - # MGDTODO - # key = w, h, self.figure.dpi.get() + l, b, w, h = self.figure.bbox.bounds key = w, h, self.figure.dpi try: self._lastKey, self.renderer except AttributeError: need_new_renderer = True Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -60,7 +60,6 @@ w,h = widget.window.get_size() if w==1 or h==1: return # empty fig - # dpival = self.figure.dpi.get() MGDTODO # compute desired figure size in inches dpival = self.figure.dpi winch = w/dpival Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -175,7 +175,6 @@ self._resize_callback(event) # compute desired figure size in inches - # dpival = self.figure.dpi.get() MGDTODO dpival = self.figure.dpi winch = width/dpival hinch = height/dpival Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/cbook.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -955,6 +955,84 @@ outstream.write("Examining: %r\n" % (obj,)) recurse(obj, obj, { }, []) +class Grouper(object): + """ + This class provides a lightweight way to group arbitrary objects + together into disjoint sets when a full-blown graph data structure + would be overkill. + + Objects can be joined using .join(), tested for connectedness + using .joined(), and all disjoint sets can be retreived using + .get(). + + The objects being joined must be hashable. + + For example: + + >>> g = grouper.Grouper() + >>> g.join('a', 'b') + >>> g.join('b', 'c') + >>> g.join('d', 'e') + >>> list(g.get()) + [['a', 'b', 'c'], ['d', 'e']] + >>> g.joined('a', 'b') + True + >>> g.joined('a', 'c') + True + >>> g.joined('a', 'd') + False""" + def __init__(self, init=[]): + mapping = self._mapping = {} + for x in init: + mapping[x] = [x] + + def join(self, a, *args): + """ + Join given arguments into the same set. + Accepts one or more arguments. + """ + mapping = self._mapping + set_a = mapping.setdefault(a, [a]) + + for arg in args: + set_b = mapping.get(arg) + if set_b is None: + set_a.append(arg) + mapping[arg] = set_a + elif set_b is not set_a: + if len(set_b) > len(set_a): + set_a, set_b = set_b, set_a + set_a.extend(set_b) + for elem in set_b: + mapping[elem] = set_a + + def joined(self, a, b): + """ + Returns True if a and b are members of the same set. + """ + mapping = self._mapping + try: + return mapping[a] is mapping[b] + except KeyError: + return False + + def __iter__(self): + """ + Returns an iterator returning each of the disjoint sets as a list. + """ + seen = set() + for elem, group in self._mapping.iteritems(): + if elem not in seen: + yield group + seen.update(group) + + def get_siblings(self, a): + """ + Returns all of the items joined with the given item, including + itself. + """ + return self._mapping.get(a, [a]) + if __name__=='__main__': assert( allequal([1,1,1]) ) assert(not allequal([1,1,0]) ) Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/figure.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -161,10 +161,8 @@ def _get_dpi(self): return self._dpi def _set_dpi(self, dpi): - print "setting dpi" self._dpi = dpi self._dpi_scale_trans.clear().scale(dpi, dpi) - print self._dpi_scale_trans dpi = property(_get_dpi, _set_dpi) def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'): @@ -178,10 +176,7 @@ bottom : the bottom of the subplots for subplots_adjust rotation: the rotation of the xtick labels ha : the horizontal alignment of the xticklabels - - """ - for ax in self.get_axes(): if not hasattr(ax, 'is_last_row'): raise RuntimeError('Axes must be subplot instances; found %s'%type(ax)) @@ -333,11 +328,8 @@ dpival = self.dpi self.bbox_inches.max = w, h - # self.figwidth.set(w) MGDTODO - # self.figheight.set(h) if forward: - # dpival = self.dpi.get() dpival = self.dpi canvasw = w*dpival canvash = h*dpival @@ -347,7 +339,6 @@ def get_size_inches(self): return self.bbox_inches.max - # return self.figwidth.get(), self.figheight.get() MGDTODO def get_edgecolor(self): 'Get the edge color of the Figure rectangle' @@ -360,7 +351,6 @@ def get_figwidth(self): 'Return the figwidth as a float' return self.bbox_inches.xmax - # return self.figwidth.get() MGDTODO def get_figheight(self): 'Return the figheight as a float' @@ -369,7 +359,6 @@ def get_dpi(self): 'Return the dpi as a float' return self.dpi - # return self.dpi.get() MGDTODO def get_frameon(self): 'get the boolean indicating frameon' @@ -397,7 +386,6 @@ ACCEPTS: float """ - # self.dpi.set(val) MGDTODO self.dpi = val def set_figwidth(self, val): @@ -406,7 +394,6 @@ ACCEPTS: float """ - # self.figwidth.set(val) MGDTODO self.bbox_inches.xmax = val def set_figheight(self, val): @@ -415,7 +402,6 @@ ACCEPTS: float """ - # MGDTODO (set()) self.bbox_inches.ymax = val def set_frameon(self, b): @@ -598,8 +584,6 @@ #print 'figure draw' if not self.get_visible(): return renderer.open_group('figure') - # MGDTODO - # self.transFigure.freeze() # eval the lazy objects if self.frameon: self.figurePatch.draw(renderer) @@ -633,8 +617,6 @@ for legend in self.legends: legend.draw(renderer) - # MGDTODO - # self.transFigure.thaw() # release the lazy objects renderer.close_group('figure') self._cachedRenderer = renderer Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/legend.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -21,7 +21,7 @@ up the legend """ from __future__ import division -import sys, warnings +import copy, sys, warnings import numpy as npy @@ -558,9 +558,7 @@ handle.set_height(h/2) # Set the data for the legend patch - # MGDTODO: This copy may no longer be needed now that Bboxes are - # essentially immutable - bbox = self._get_handle_text_bbox(renderer).copy() + bbox = copy.copy(self._get_handle_text_bbox(renderer)) bbox = bbox.scaled(1 + self.pad, 1 + self.pad) l,b,w,h = bbox.get_bounds() Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -352,32 +352,18 @@ self._picker = p def get_window_extent(self, renderer): - self._newstyle = hasattr(renderer, 'draw_markers') - if self._newstyle: - x = self._x - y = self._y - else: - x, y = self._get_plottable() + xys = self.get_transform()(self._xys) - # MGDTODO: Put this in a single Nx2 array, rather than these - # separate ones - #### Conversion code - a = npy.vstack((x, y)).swapaxes(0, 1) - #### - x, y = self.get_transform()(a) - print "get_window_extent", self.get_transform() - - #x, y = self.get_transform().seq_x_y(x, y) + x = xys[:, 0] + y = xys[:, 1] + left = x.min() + bottom = y.min() + width = x.max() - left + height = y.max() - bottom - left = min(x) - bottom = min(y) - width = max(x) - left - height = max(y) - bottom - # correct for marker size, if any if self._marker is not None: - ms = self._markersize/72.0*self.figure.dpi - # ms = self._markersize/72.0*self.figure.dpi.get() MGDTODO + ms = self._markersize / 72.0 * self.figure.dpi left -= ms/2 bottom -= ms/2 width += ms @@ -411,6 +397,7 @@ def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units + # MGDTODO: Deal with units x = ma.asarray(self.convert_xunits(self._xorig), float) y = ma.asarray(self.convert_yunits(self._yorig), float) @@ -435,15 +422,15 @@ else: self._segments = None - self._x = npy.asarray(x, float) - self._y = npy.asarray(y, float) - self._path = Path(npy.vstack((self._x, self._y)).transpose(), - closed=False) + 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._path = Path(self._xy, closed=False) self._logcache = None - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-18 19:29:21 UTC (rev 3856) @@ -19,9 +19,9 @@ self._parents = Set() def invalidate(self): - if not self._do_invalidation(): - for parent in self._parents: - parent.invalidate() + self._do_invalidation() + for parent in self._parents: + parent.invalidate() def _do_invalidation(self): return False @@ -187,14 +187,16 @@ return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ - # JDH: the update method will update the box limits from the - # existing limits and the new data; it appears here you are just - # using the new data. We use an "ignore" flag to specify whether - # you want to include the existing data or not in the update def update_from_data(self, x, y, ignore=True): - self._points = npy.array( - [[x.min(), y.min()], [x.max(), y.max()]], - npy.float_) + if ignore: + self._points = npy.array( + [[x.min(), y.min()], [x.max(), y.max()]], + npy.float_) + else: + self._points = npy.array( + [[min(x.min(), self.xmin), min(y.min(), self.ymin)], + [max(x.max(), self.xmax), max(y.max(), self.ymax)]], + npy.float_) self.invalidate() # MGDTODO: Probably a more efficient ways to do this... @@ -409,9 +411,7 @@ return self.get_matrix() def _do_invalidation(self): - result = self._inverted is None self._inverted = None - return result #@staticmethod def _concat(a, b): @@ -494,6 +494,7 @@ if matrix is None: matrix = npy.identity(3) else: + matrix = npy.asarray(matrix, npy.float_) assert matrix.shape == (3, 3) self._mtx = matrix self._inverted = None @@ -629,8 +630,6 @@ if self._mtx is not None: self._mtx = None Affine2DBase._do_invalidation(self) - return False - return True def is_separable(self): return True @@ -684,7 +683,7 @@ def _do_invalidation(self): self._mtx = None - Affine2DBase._do_invalidation(self) + return Affine2DBase._do_invalidation(self) def get_matrix(self): if self._mtx is None: @@ -718,8 +717,6 @@ if self._mtx is not None: self._mtx = None Affine2DBase._do_invalidation(self) - return False - return True def is_separable(self): return True Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-18 16:21:37 UTC (rev 3855) +++ branches/transforms/src/_backend_agg.cpp 2007-09-18 19:29:21 UTC (rev 3856) @@ -456,7 +456,7 @@ //return the agg::rect for bbox, flipping y PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2); - if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) + if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) throw Py::TypeError ("Expected a Bbox object."); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-18 16:21:40
|
Revision: 3855 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3855&view=rev Author: mdboom Date: 2007-09-18 09:21:37 -0700 (Tue, 18 Sep 2007) Log Message: ----------- More code using new transformation framework. Lots of dead code removed from backend_agg.cpp/h Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/lib/matplotlib/type1font.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-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -39,8 +39,7 @@ def _get_cached_native_path(self, path): native_path = self._native_paths.get(path) if native_path is None: - import matplotlib.patches - 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/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -121,13 +121,9 @@ debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', 'debug-annoying') - # self.draw_polygon = self._renderer.draw_polygon - self.draw_rectangle = self._renderer.draw_rectangle - # MGDTODO -- remove these lines - # self.draw_lines = self._renderer.draw_lines - # self.draw_markers = self._renderer.draw_markers - self.draw_image = self._renderer.draw_image + self.convert_to_native_path = self._renderer.convert_to_native_path + self.draw_image = self._renderer.draw_image self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region self.mathtext_parser = MathTextParser('Agg') @@ -137,12 +133,9 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') - def convert_to_native_path(self, path): - return self._renderer.convert_to_native_path(path.vertices, path.codes) - - def _draw_native_path(self, gc, native_path, transform, rgbFace): - return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace) - + def _draw_native_path(self, gc, path, transform, rgbFace): + return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace) + def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): """ Draw an arc centered at x,y with width and height and angles @@ -175,8 +168,8 @@ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): return self._renderer.draw_markers( gc, - native_marker_path, marker_trans.to_values(), - path.vertices, path.codes, trans.to_values(), + native_marker_path, marker_trans.get_matrix(), + path.vertices, path.codes, trans.get_matrix(), rgbFace) def draw_polygon(self, *args): Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -712,6 +712,7 @@ def _draw_nothing(self, renderer, gc, path): pass + def _draw_steps(self, renderer, gc, xt, yt): # MGDTODO: This is a quirky one. The step-plotting part # should probably be moved to where the path is generated @@ -729,6 +730,7 @@ else: renderer.draw_lines(gc, xt2, yt2) + def _draw_solid(self, renderer, gc, path): gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) @@ -753,8 +755,15 @@ def _draw_point(self, renderer, gc, path): - self._draw_circle(renderer, gc, path, point = True) + w = renderer.points_to_pixels(self._markersize) * \ + self._point_size_reduction * 0.5 + rgbFace = self._get_rgb_face() + transform = Affine2D().scale(w) + renderer.draw_markers( + gc, Path.unit_circle(), transform, path, self.get_transform(), + rgbFace) + def _draw_pixel(self, renderer, gc, path): rgbFace = self._get_rgb_face() transform = Affine2D().translate(-0.5, -0.5) @@ -762,12 +771,8 @@ path, self.get_transform(), rgbFace) - def _draw_circle(self, renderer, gc, path, point=False): - w = renderer.points_to_pixels(self._markersize) - if point: - w *= self._point_size_reduction - w *= 0.5 - + def _draw_circle(self, renderer, gc, path): + w = renderer.points_to_pixels(self._markersize) * 0.5 rgbFace = self._get_rgb_face() transform = Affine2D().scale(w, w) renderer.draw_markers( @@ -826,7 +831,8 @@ def _draw_thin_diamond(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset) + transform = Affine2D().translate(0.5, 0.5) \ + .rotate_deg(45).scale(offset * 0.8, offset) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_rectangle(), transform, path, self.get_transform(), rgbFace) @@ -840,7 +846,7 @@ path, self.get_transform(), rgbFace) - def _draw_hexagon1(self, renderer, gc, path, point=False): + def _draw_hexagon1(self, renderer, gc, path): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) rgbFace = self._get_rgb_face() @@ -848,50 +854,44 @@ path, self.get_transform(), rgbFace) - def _draw_hexagon2(self, renderer, gc, xt, yt): + def _draw_hexagon2(self, renderer, gc, path): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset).rotate_deg(30) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, path, self.get_transform(), rgbFace) - - def _draw_vline(self, renderer, gc, xt, yt): + + _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]], closed=False) + def _draw_vline(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, -offset) - path.line_to(0, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y-offset, x, y+offset) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._line_marker_path, transform, + path, self.get_transform()) - def _draw_hline(self, renderer, gc, xt, yt): + def _draw_hline(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to(offset, 0) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x+offset, y) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._line_marker_path, transform, + path, self.get_transform()) + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) def _draw_tickleft(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - marker_transform = Affine2D().scale(offset, 1.0) + marker_transform = Affine2D().scale(-offset, 1.0) renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, path, self.get_transform()) + def _draw_tickright(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - marker_transform = Affine2D().scale(-offset, 1.0) + marker_transform = Affine2D().scale(offset, 1.0) renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, path, self.get_transform()) + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) def _draw_tickup(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) @@ -899,174 +899,99 @@ renderer.draw_markers(gc, self._tickvert_path, marker_transform, path, self.get_transform()) + def _draw_tickdown(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, -offset) renderer.draw_markers(gc, self._tickvert_path, marker_transform, path, self.get_transform()) - def _draw_plus(self, renderer, gc, xt, yt): + + _plus_path = Path([[-1.0, 0.0], [1.0, 0.0], + [0.0, -1.0], [0.0, 1.0]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) + def _draw_plus(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - if self._newstyle: + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._plus_path, transform, + path, self.get_transform()) - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to( offset, 0) - path.move_to( 0, -offset) - path.line_to( 0, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x+offset, y) - renderer.draw_line(gc, x, y-offset, x, y+offset) - def _draw_tri_down(self, renderer, gc, xt, yt): + _tri_path = Path([[0.0, 0.0], [0.0, -1.0], + [0.0, 0.0], [0.8, 0.5], + [0.0, 0.0], [-0.8, 0.5]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) + def _draw_tri_down(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(0, -offset) - path.move_to(0, 0) - path.line_to(offset1, offset2) - path.move_to(0, 0) - path.line_to(-offset1, offset2) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x, y-offset) - renderer.draw_line(gc, x, y, x+offset1, y+offset2) - renderer.draw_line(gc, x, y, x-offset1, y+offset2) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_up(self, renderer, gc, xt, yt): + + def _draw_tri_up(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(0, offset) - path.move_to(0, 0) - path.line_to(offset1, -offset2) - path.move_to(0, 0) - path.line_to(-offset1, -offset2) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x, y+offset) - renderer.draw_line(gc, x, y, x+offset1, y-offset2) - renderer.draw_line(gc, x, y, x-offset1, y-offset2) + transform = Affine2D().scale(offset).rotate_deg(180) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_left(self, renderer, gc, xt, yt): - offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(-offset, 0) - path.move_to(0, 0) - path.line_to(offset2, offset1) - path.move_to(0, 0) - path.line_to(offset2, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x-offset, y) - renderer.draw_line(gc, x, y, x+offset2, y+offset1) - renderer.draw_line(gc, x, y, x+offset2, y-offset1) + + def _draw_tri_left(self, renderer, gc, path): + offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_tri_right(self, renderer, gc, xt, yt): - offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = offset*0.8 - offset2 = offset*0.5 - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0) - path.line_to(offset, 0) - path.move_to(0, 0) - path.line_to(-offset2, offset1) - path.move_to(0, 0) - path.line_to(-offset2, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x+offset, y) - renderer.draw_line(gc, x, y, x-offset2, y+offset1) - renderer.draw_line(gc, x, y, x-offset2, y-offset1) + + def _draw_tri_right(self, renderer, gc, path): + offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(270) + renderer.draw_markers(gc, self._tri_path, transform, + path, self.get_transform()) - def _draw_caretdown(self, renderer, gc, xt, yt): + + _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]], closed=False) + def _draw_caretdown(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, offset1) - path.line_to(0, 0) - path.line_to(+offset, offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y+offset1, x, y) - renderer.draw_line(gc, x, y, x+offset, y+offset1) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretup(self, renderer, gc, xt, yt): + + def _draw_caretup(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, -offset1) - path.line_to(0, 0) - path.line_to(+offset, -offset1) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y-offset1, x, y) - renderer.draw_line(gc, x, y, x+offset, y-offset1) + transform = Affine2D().scale(offset).rotate_deg(180) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretleft(self, renderer, gc, xt, yt): + + def _draw_caretleft(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(offset1, -offset) - path.line_to(0, 0) - path.line_to(offset1, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x+offset1, y-offset, x, y) - renderer.draw_line(gc, x, y, x+offset1, y+offset) + transform = Affine2D().scale(offset).rotate_deg(90) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) - def _draw_caretright(self, renderer, gc, xt, yt): + + def _draw_caretright(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) - offset1 = 1.5*offset - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset1, -offset) - path.line_to(0, 0) - path.line_to(-offset1, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset1, y-offset, x, y) - renderer.draw_line(gc, x, y, x-offset1, y+offset) + transform = Affine2D().scale(offset).rotate_deg(270) + renderer.draw_markers(gc, self._caret_path, transform, + path, self.get_transform()) + + _x_path = Path([[-1.0, -1.0], [1.0, 1.0], + [-1.0, 1.0], [1.0, -1.0]], + [Path.MOVETO, Path.LINETO, + Path.MOVETO, Path.LINETO]) def _draw_x(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + renderer.draw_markers(gc, self._x_path, transform, + path, self.get_transform()) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, -offset) - path.line_to(offset, offset) - path.move_to(-offset, offset) - path.line_to(offset, -offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y-offset, x+offset, y+offset) - renderer.draw_line(gc, x-offset, y+offset, x+offset, y-offset) - + def update_from(self, other): 'copy properties from other to self' Artist.update_from(self, other) Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/path.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -50,6 +50,9 @@ i += NUM_VERTICES[code] assert i == len(self.vertices) + def __repr__(self): + return "Path(%s, %s)" % (self.vertices, self.codes) + def _get_codes(self): return self._codes codes = property(_get_codes) Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -88,11 +88,13 @@ intervaly = property(_get_intervaly) def _get_width(self): - return self.xmax - self.xmin + points = self.get_points() + return points[1, 0] - points[0, 0] width = property(_get_width) def _get_height(self): - return self.ymax - self.ymin + points = self.get_points() + return points[1, 1] - points[0, 1] height = property(_get_height) def _get_bounds(self): Modified: branches/transforms/lib/matplotlib/type1font.py =================================================================== --- branches/transforms/lib/matplotlib/type1font.py 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/lib/matplotlib/type1font.py 2007-09-18 16:21:37 UTC (rev 3855) @@ -1,15 +1,15 @@ """ A class representing a Type 1 font. -This version merely allows reading in pfa and pfb files, stores the -data in pfa format, and allows reading the parts of the data in a -format suitable for embedding in pdf files. A more complete class -might support subsetting. +This version merely reads pfa and pfb files and splits them for +embedding in pdf files. There is no support yet for subsetting or +anything like that. -Usage: font = Type1Font(filename) - somefile.write(font.data) # writes out font in pfa format - len1, len2, len3 = font.lengths() # needed for pdf embedding +Usage (subject to change): + font = Type1Font(filename) + clear_part, encrypted_part, finale = font.parts + Source: Adobe Technical Note #5040, Supporting Downloadable PostScript Language Fonts. @@ -32,8 +32,7 @@ def _read(self, file): rawdata = file.read() if not rawdata.startswith(chr(128)): - self.data = rawdata - return + return rawdata data = '' while len(rawdata) > 0: @@ -101,4 +100,6 @@ if __name__ == '__main__': import sys font = Type1Font(sys.argv[1]) - sys.stdout.write(font.data) + parts = font.parts + print len(parts[0]), len(parts[1]), len(parts[2]) + Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-17 13:41:38 UTC (rev 3854) +++ branches/transforms/src/_backend_agg.cpp 2007-09-18 16:21:37 UTC (rev 3855) @@ -10,7 +10,6 @@ #include <time.h> #include <algorithm> - #include "agg_conv_transform.h" #include "agg_conv_curve.h" #include "agg_scanline_storage_aa.h" @@ -43,28 +42,48 @@ #define M_PI_2 1.57079632679489661923 #endif -agg::trans_affine py_sequence_to_agg_transformation_matrix(const Py::Object& obj) { - Py::SeqBase<Py::Float> seq; +/** A helper function to convert from a Numpy affine transformation matrix + * to an agg::trans_affine. + */ +agg::trans_affine py_to_agg_transformation_matrix(const Py::Object& obj) { + PyArrayObject* matrix = NULL; + + double a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0; + try { - seq = obj; - } catch(...) { - throw Py::ValueError("Transformation matrix must be given as a 6-element list."); - } + matrix = (PyArrayObject*) PyArray_ContiguousFromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!matrix || matrix->nd != 2 || matrix->dimensions[0] != 3 || matrix->dimensions[1] != 3) { + throw Py::ValueError("Invalid affine transformation matrix."); + } - if (seq.size() != 6) { - throw Py::ValueError("Transformation matrix must be given as a 6-element list."); + size_t stride0 = matrix->strides[0]; + size_t stride1 = matrix->strides[1]; + char* row0 = matrix->data; + char* row1 = row0 + stride0; + + a = *(double*)(row0); + row0 += stride1; + c = *(double*)(row0); + row0 += stride1; + e = *(double*)(row0); + + b = *(double*)(row1); + row1 += stride1; + d = *(double*)(row1); + row1 += stride1; + f = *(double*)(row1); + } catch (...) { + Py_XDECREF(matrix); } - return agg::trans_affine - (Py::Float(seq[0]), - Py::Float(seq[1]), - Py::Float(seq[2]), - Py::Float(seq[3]), - Py::Float(seq[4]), - Py::Float(seq[5])); + Py_XDECREF(matrix); + + return agg::trans_affine(a, b, c, d, e, f); } -// MGDTODO: Implement this as a nice iterator +/** Helper function to get the next vertex in a Numpy array of vertices. + * Will generally be used through the GET_NEXT_VERTEX macro. + */ inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, double& x, double& y, size_t next_vertex_stride, @@ -78,12 +97,18 @@ #define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) +Py::Object BufferRegion::to_string(const Py::Tuple &args) { + + // owned=true to prevent memory leak + return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); +} + + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), Ndash(0), dashOffset(0.0), dasha(NULL) { - _VERBOSE("GCAgg::GCAgg"); linewidth = points_to_pixels ( gc.getAttr("_linewidth") ) ; alpha = Py::Float( gc.getAttr("_alpha") ); @@ -100,7 +125,6 @@ GCAgg::_set_antialiased(const Py::Object& gc) { _VERBOSE("GCAgg::antialiased"); isaa = Py::Int( gc.getAttr( "_antialiased") ); - } agg::rgba @@ -123,13 +147,12 @@ return p * dpi/72.0; } - void GCAgg::_set_linecap(const Py::Object& gc) { _VERBOSE("GCAgg::_set_linecap"); std::string capstyle = Py::String( gc.getAttr( "_capstyle" ) ); - + if (capstyle=="butt") cap = agg::butt_cap; else if (capstyle=="round") @@ -138,7 +161,6 @@ cap = agg::square_cap; else throw Py::ValueError(Printf("GC _capstyle attribute must be one of butt, round, projecting; found %s", capstyle.c_str()).str()); - } void @@ -155,7 +177,6 @@ join = agg::bevel_join; else throw Py::ValueError(Printf("GC _joinstyle attribute must be one of butt, round, projecting; found %s", joinstyle.c_str()).str()); - } void @@ -193,7 +214,7 @@ } } - +// MGDTODO: Convert directly from Bbox object (numpy) void GCAgg::_set_clip_rectangle( const Py::Object& gc) { //set the clip rectangle from the gc @@ -204,7 +225,7 @@ cliprect = NULL; Py::Object o ( gc.getAttr( "_cliprect" ) ); - if (o.ptr()==Py_None) { + if (o.ptr() == Py_None) { return; } @@ -229,38 +250,18 @@ _VERBOSE("GCAgg::_set_clip_path"); - delete clippath; + Py_XINCREF(clippath); clippath = NULL; - Py::Object o = gc.getAttr( "_clippath" ); + Py::Object o = gc.getAttr("_clippath"); if (o.ptr()==Py_None) { return; } - agg::path_storage *tmppath; - swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *"); - assert(descr); - if (SWIG_ConvertPtr(o.ptr(),(void **)(&tmppath), descr, 0) == -1) { - throw Py::TypeError("Could not convert gc path_storage"); - } - - tmppath->rewind(0); - clippath = new agg::path_storage(); - clippath->copy_from(*tmppath); - clippath->rewind(0); - tmppath->rewind(0); + clippath = new PathAgg(o); } -Py::Object BufferRegion::to_string(const Py::Tuple &args) { - - // owned=true to prevent memory leak - return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true); -} - - - - const size_t RendererAgg::PIXELS_PER_INCH(96); @@ -311,19 +312,14 @@ void -RendererAgg::set_clipbox_rasterizer( double *cliprect) { +RendererAgg::set_clipbox_rasterizer(double *cliprect) { //set the clip rectangle from the gc _VERBOSE("RendererAgg::set_clipbox_rasterizer"); - theRasterizer->reset_clipping(); rendererBase->reset_clipping(true); - //if (cliprect==NULL) { - // theRasterizer->reset_clipping(); - // rendererBase->reset_clipping(true); - //} if (cliprect!=NULL) { double l = cliprect[0] ; @@ -355,273 +351,10 @@ } -// MGDTODO: Remove this method (it has been conglomerated into draw_path -template <class VS> -void -RendererAgg::_fill_and_stroke(VS& path, - const GCAgg& gc, - const facepair_t& face, - bool curvy) { - typedef agg::conv_curve<VS> curve_t; - - //bool isclippath(gc.clippath!=NULL); - //if (isclippath) _process_alpha_mask(gc); - - if (face.first) { - rendererAA->color(face.second); - if (curvy) { - curve_t curve(path); - theRasterizer->add_path(curve); - } - else - theRasterizer->add_path(path); - - /* - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - */ - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - - //now stroke the edge - if (gc.linewidth) { - if (curvy) { - curve_t curve(path); - agg::conv_stroke<curve_t> stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - else { - agg::conv_stroke<VS> stroke(path); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - - - /* - if ( gc.isaa ) { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - } - else { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else{ - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } - - */ - - if ( gc.isaa ) { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - else { - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } - - -} - -Py::Object -RendererAgg::draw_rectangle(const Py::Tuple & args) { - _VERBOSE("RendererAgg::draw_rectangle"); - args.verify_length(6); - - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - - double l = Py::Float( args[2] ); - double b = Py::Float( args[3] ); - double w = Py::Float( args[4] ); - double h = Py::Float( args[5] ); - - b = height - (b+h); - double r = l + w; - double t = b + h; - - //snapto pixel centers - l = (int)l + 0.5; - b = (int)b + 0.5; - r = (int)r + 0.5; - t = (int)t + 0.5; - - - set_clipbox_rasterizer(gc.cliprect); - - agg::path_storage path; - - - path.move_to(l, t); - path.line_to(r, t); - path.line_to(r, b); - path.line_to(l, b); - path.close_polygon(); - - _fill_and_stroke(path, gc, face, false); - - return Py::Object(); - -} - -Py::Object -RendererAgg::draw_ellipse(const Py::Tuple& args) { - _VERBOSE("RendererAgg::draw_ellipse"); - args.verify_length(7); - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - double x = Py::Float( args[2] ); - double y = Py::Float( args[3] ); - double w = Py::Float( args[4] ); - double h = Py::Float( args[5] ); - double rot = Py::Float( args[6] ); - - double r; // rot in radians - - set_clipbox_rasterizer(gc.cliprect); - - // Approximate the ellipse with 4 bezier paths - agg::path_storage path; - if (rot == 0.0) // simple case - { - path.move_to(x, height-(y+h)); - path.arc_to(w, h, 0.0, false, true, x+w, height-y); - path.arc_to(w, h, 0.0, false, true, x, height-(y-h)); - path.arc_to(w, h, 0.0, false, true, x-w, height-y); - path.arc_to(w, h, 0.0, false, true, x, height-(y+h)); - path.close_polygon(); - } - else // rotate by hand :( - { - // deg to rad - r = rot * (M_PI/180.0); - path.move_to( x+(cos(r)*w), height-(y+(sin(r)*w))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2*3)*h), height-(y+(sin(r+M_PI_2*3)*h))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI)*w), height-(y+(sin(r+M_PI)*w))); - path.arc_to(w, h, -r, false, true, x+(cos(r+M_PI_2)*h), height-(y+(sin(r+M_PI_2)*h))); - path.arc_to(w, h, -r, false, true, x+(cos(r)*w), height-(y+(sin(r)*w))); - path.close_polygon(); - } - - _fill_and_stroke(path, gc, face); - return Py::Object(); - -} - -Py::Object -RendererAgg::draw_polygon(const Py::Tuple& args) { - _VERBOSE("RendererAgg::draw_polygon"); - - args.verify_length(3); - - GCAgg gc = GCAgg(args[0], dpi); - facepair_t face = _get_rgba_face(args[1], gc.alpha); - - Py::SeqBase<Py::Object> points( args[2] ); - - set_clipbox_rasterizer(gc.cliprect); - - size_t Npoints = points.length(); - if (Npoints<=0) - return Py::Object(); - - - // dump the x.y vertices into a double array for faster look ahead - // and behind access - double *xs = new double[Npoints]; - double *ys = new double[Npoints]; - - for (size_t i=0; i<Npoints; i++) { - Py::SeqBase<Py::Object> xy(points[i]); - xy = Py::Tuple(points[i]); - xs[i] = Py::Float(xy[0]); - ys[i] = Py::Float(xy[1]); - ys[i] = height - ys[i]; - } - - - - agg::path_storage path; - for (size_t j=0; j<Npoints; j++) { - - double x = xs[j]; - double y = ys[j]; - - //snapto pixel centers - x = (int)x + 0.5; - y = (int)y + 0.5; - - if (j==0) path.move_to(x,y); - else path.line_to(x,y); - } - path.close_polygon(); - - _fill_and_stroke(path, gc, face, false); - - delete [] xs; - delete [] ys; - - _VERBOSE("RendererAgg::draw_polygon DONE"); - return Py::Object(); - -} - - - SnapData SafeSnap::snap (const float& x, const float& y) { xsnap = (int)x + 0.5; ysnap = (int)y + 0.5; - - if ( first || ( (xsnap!=lastxsnap) || (ysnap!=lastysnap) ) ) { lastxsnap = xsnap; @@ -690,9 +423,6 @@ rb.copy_from(*renderingBuffer, &r, -r.x1, -r.y1); BufferRegion* reg = new BufferRegion(buf, r, true); return Py::asObject(reg); - - - } Py::Object @@ -715,25 +445,20 @@ rendererBase->copy_from(rbuf, 0, region->rect.x1, region->rect.y1); return Py::Object(); - - - } - +/** + * Helper function to convert a Python Bbox object to an agg rectangle + */ template<class T> agg::rect_base<T> RendererAgg::bbox_to_rect(const Py::Object& o) { //return the agg::rect for bbox, flipping y PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2); - if (!bbox) + if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) throw Py::TypeError ("Expected a Bbox object."); - - if (bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2) - throw Py::TypeError - ("Expected a Bbox object."); double l = bbox->data[0]; double b = bbox->data[1]; @@ -805,469 +530,8 @@ return numIntersect; } -void RendererAgg::DrawQuadMesh(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[]) -{ - /* draw each quadrilateral */ - // agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase); - int i = 0; - int j = 0; - int k = 0; - double xs[4]; - double ys[4]; - int col[4]; - int numCol; - double ymin; - int firstRow; - double ymax; - int lastRow; - for(i=0; i < meshHeight; i++) - { - for(j=0; j < meshWidth; j++) - { - //currTime = clock(); - xs[0] = xCoords[(i * (meshWidth + 1)) + j]; - ys[0] = yCoords[(i * (meshWidth + 1)) + j]; - xs[1] = xCoords[(i * (meshWidth + 1)) + j+1]; - ys[1] = yCoords[(i * (meshWidth + 1)) + j+1]; - xs[3] = xCoords[((i+1) * (meshWidth + 1)) + j]; - ys[3] = yCoords[((i+1) * (meshWidth + 1)) + j]; - xs[2] = xCoords[((i+1) * (meshWidth + 1)) + j+1]; - ys[2] = yCoords[((i+1) * (meshWidth + 1)) + j+1]; - ymin = std::min(std::min(std::min(ys[0], ys[1]), ys[2]), ys[3]); - ymax = std::max(std::max(std::max(ys[0], ys[1]), ys[2]), ys[3]); - firstRow = (int)(ymin); - lastRow = (int)(ymax); - //timer1 += (clock() - currTime); - //currTime = clock(); - //timer2 += (clock() - currTime); - //currTime = clock(); - for(k = firstRow; k <= lastRow; k++) - { - numCol = inPolygon(k, xs, ys, col); - if (numCol >= 2) rendererBase->copy_hline(col[0], k, col[1] - 1, colorArray[(i * meshWidth) + j]); - if (numCol == 4) rendererBase->copy_hline(col[2], k, col[3] - 1, colorArray[(i * meshWidth) + j]); - } - } - } - return; -} -void RendererAgg::DrawQuadMeshEdges(int meshWidth, int meshHeight, const agg::rgba8 colorArray[], const double xCoords[], const double yCoords[]) -{ - int i, j; - agg::renderer_primitives<agg::renderer_base<agg::pixfmt_rgba32> > lineRen(*rendererBase); - agg::rgba8 lc(0, 0, 0, 32); - lineRen.line_color(lc); - /* show the vertical edges */ - for(i=0; i <= meshWidth; i++) - { - lineRen.move_to((int)(256.0 * (xCoords[i])), (int)(256.0 * (yCoords[i]))); - for(j=1; j <= meshHeight; j++) - lineRen.line_to((int)(256.0 *(xCoords[(j * (meshWidth + 1))+i])), (int)(256.0 * (yCoords[(j * (meshWidth + 1))+i]))); - } - /* show the horizontal edges */ - for(i=0; i <= meshHeight; i++) - { - lineRen.move_to((int)(256.0 * (xCoords[i * (meshWidth + 1)])), (int)(256.0 * (yCoords[i * (meshWidth + 1)]))); - for(j=1; j <= meshWidth; j++) - lineRen.line_to((int)(256.0 * (xCoords[(i * (meshWidth + 1))+j])), (int)(256.0 * (yCoords[(i * (meshWidth + 1))+j]))); - } -} - - - Py::Object -RendererAgg::draw_lines(const Py::Tuple& args) { - - _VERBOSE("RendererAgg::draw_lines"); - args.verify_length(4); - - Py::Object xo = args[1]; - Py::Object yo = args[2]; - - PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1); - - if (xa==NULL) - throw Py::TypeError("RendererAgg::draw_lines expected numerix array"); - - - PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1); - - if (ya==NULL) - throw Py::TypeError("RendererAgg::draw_lines expected numerix array"); - - - size_t Nx = xa->dimensions[0]; - size_t Ny = ya->dimensions[0]; - - if (Nx!=Ny) - throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str()); - - // call gc with snapto==True if line len is 2 to fix grid line - // problem - bool snapto = false; - if (Nx==2) { - // disable subpiel rendering for len(2) horizontal or vertical - // lines - double x0 = *(double *)(xa->data + 0*xa->strides[0]); - double x1 = *(double *)(xa->data + 1*xa->strides[0]); - double y0 = *(double *)(ya->data + 0*ya->strides[0]); - double y1 = *(double *)(ya->data + 1*ya->strides[0]); - snapto = (x0==x1) || (y0==y1); - - } - GCAgg gc = GCAgg(args[0], dpi, snapto); - - set_clipbox_rasterizer(gc.cliprect); - //path_t transpath(path, xytrans); - _process_alpha_mask(gc); - - agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[3]); - - agg::path_storage path; - - // MGDTODO - bool needNonlinear = false; - // mpltransform->need_nonlinear_api(); - - double thisx(0.0), thisy(0.0); - double origdx(0.0), origdy(0.0), origdNorm2(0); - bool moveto = true; - double heightd = height; - - double lastx(0), lasty(0); - double lastWrittenx(0), lastWritteny(0); - bool clipped = false; - - bool haveMin = false, lastMax = true; - double dnorm2Min(0), dnorm2Max(0); - double maxX(0), maxY(0), minX(0), minY(0); - - double totdx, totdy, totdot; - double paradx, parady, paradNorm2; - double perpdx, perpdy, perpdNorm2; - - int counter = 0; - //idea: we can skip drawing many lines: lines < 1 pixel in length, lines - //outside of the drawing area, and we can combine sequential parallel lines - //into a single line instead of redrawing lines over the same points. - //The loop below works a bit like a state machine, where what it does depends - //on what it did in the last looping. To test whether sequential lines - //are close to parallel, I calculate the distance moved perpendicular to the - //last line. Once it gets too big, the lines cannot be combined. - for (size_t i=0; i<Nx; i++) { - - thisx = *(double *)(xa->data + i*xa->strides[0]); - thisy = *(double *)(ya->data + i*ya->strides[0]); - - if (needNonlinear) - try { - // MGDTODO - // mpltransform->nonlinear_only_api(&thisx, &thisy); - } - catch (...) { - moveto = true; - continue; - } - if (MPL_isnan64(thisx) || MPL_isnan64(thisy)) { - moveto = true; - continue; - } - - //use agg's transformer? - xytrans.transform(&thisx, &thisy); - thisy = heightd - thisy; //flipy - - if (snapto) { - //disable subpixel rendering for horizontal or vertical lines of len=2 - //because it causes irregular line widths for grids and ticks - thisx = (int)thisx + 0.5; - thisy = (int)thisy + 0.5; - } - - //if we are starting a new path segment, move to the first point + init - if(moveto){ - path.move_to(thisx, thisy); - lastx = thisx; - lasty = thisy; - origdNorm2 = 0; //resets the orig-vector variables (see if-statement below) - moveto = false; - continue; - } - - //don't render line segments less that on pixel long! - if (fabs(thisx-lastx) < 1.0 && fabs(thisy-lasty) < 1.0 ){ - continue; //don't update lastx this time! - } - - //skip any lines that are outside the drawing area. Note: More lines - //could be clipped, but a more involved calculation would be needed - if( (thisx < 0 && lastx < 0 ) || - (thisx > width && lastx > width ) || - (thisy < 0 && lasty < 0 ) || - (thisy > height && lasty > height) ){ - lastx = thisx; - lasty = thisy; - clipped = true; - continue; - } - - //if we have no orig vector, set it to this vector and continue. - //this orig vector is the reference vector we will build up the line to - if(origdNorm2 == 0){ - //if we clipped after the moveto but before we got here, redo the moveto - if(clipped){ - path.move_to(lastx, lasty); - clipped = false; - } - - origdx = thisx - lastx; - origdy = thisy - lasty; - origdNorm2 = origdx*origdx + origdy*origdy; - - //set all the variables to reflect this new orig vecor - dnorm2Max = origdNorm2; - dnorm2Min = 0; - haveMin = false; - lastMax = true; - maxX = thisx; - maxY = thisy; - minX = lastx; - minY = lasty; - - lastWrittenx = lastx; - lastWritteny = lasty; - - //set the last point seen - lastx = thisx; - lasty = thisy; - continue; - } - - //if got to here, then we have an orig vector and we just got - //a vector in the sequence. - - //check that the perpendicular distance we have moved from the - //last written point compared to the line we are building is not too - //much. If o is the orig vector (we are building on), and v is the vector - //from the last written point to the current point, then the perpendicular - //vector is p = v - (o.v)o, and we normalize o (by dividing the - //second term by o.o). - - //get the v vector - totdx = thisx - lastWrittenx; - totdy = thisy - lastWritteny; - totdot = origdx*totdx + origdy*totdy; - - //get the para vector ( = (o.v)o/(o.o) ) - paradx = totdot*origdx/origdNorm2; - parady = totdot*origdy/origdNorm2; - paradNorm2 = paradx*paradx + parady*parady; - - //get the perp vector ( = v - para ) - perpdx = totdx - paradx; - perpdy = totdy - parady; - perpdNorm2 = perpdx*perpdx + perpdy*perpdy; - - //if the perp vector is less than some number of (squared) pixels in size, - //then merge the current vector - if(perpdNorm2 < 0.25 ){ - //check if the current vector is parallel or - //anti-parallel to the orig vector. If it is parallel, test - //if it is the longest of the vectors we are merging in that direction. - //If anti-p, test if it is the longest in the opposite direction (the - //min of our final line) - - lastMax = false; - if(totdot >= 0){ - if(paradNorm2 > dnorm2Max){ - lastMax = true; - dnorm2Max = paradNorm2; - maxX = lastWrittenx + paradx; - maxY = lastWritteny + parady; - } - } - else{ - - haveMin = true; - if(paradNorm2 > dnorm2Min){ - dnorm2Min = paradNorm2; - minX = lastWrittenx + paradx; - minY = lastWritteny + parady; - } - } - - lastx = thisx; - lasty = thisy; - continue; - } - - //if we get here, then this vector was not similar enough to the line - //we are building, so we need to draw that line and start the next one. - - //if the line needs to extend in the opposite direction from the direction - //we are drawing in, move back to we start drawing from back there. - if(haveMin){ - path.line_to(minX, minY); //would be move_to if not for artifacts - } - - path.line_to(maxX, maxY); - - //if we clipped some segments between this line and the next line - //we are starting, we also need to move to the last point. - if(clipped){ - path.move_to(lastx, lasty); - } - else if(!lastMax){ - //if the last line was not the longest line, then move back to the end - //point of the last line in the sequence. Only do this if not clipped, - //since in that case lastx,lasty is not part of the line just drawn. - path.line_to(lastx, lasty); //would be move_to if not for artifacts - } - - //std::cout << "draw lines (" << lastx << ", " << lasty << ")" << std::endl; - - //now reset all the variables to get ready for the next line - - origdx = thisx - lastx; - origdy = thisy - lasty; - origdNorm2 = origdx*origdx + origdy*origdy; - - dnorm2Max = origdNorm2; - dnorm2Min = 0; - haveMin = false; - lastMax = true; - maxX = thisx; - maxY = thisy; - minX = lastx; - minY = lasty; - - lastWrittenx = lastx; - lastWritteny = lasty; - - clipped = false; - - lastx = thisx; - lasty = thisy; - - counter++; - } - - //draw the last line, which is usually not drawn in the loop - if(origdNorm2 != 0){ - if(haveMin){ - path.line_to(minX, minY); //would be move_to if not for artifacts - } - - path.line_to(maxX, maxY); - } - - //std::cout << "drew " << counter+1 << " lines" << std::endl; - - Py_XDECREF(xa); - Py_XDECREF(ya); - - //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t; - //path_t transpath(path, xytrans); - _VERBOSE("RendererAgg::draw_lines rendering lines path"); - _render_lines_path(path, gc); - - _VERBOSE("RendererAgg::draw_lines DONE"); - return Py::Object(); - -} - -bool -RendererAgg::_process_alpha_mask(const GCAgg& gc) - //if gc has a clippath set, process the alpha mask and return True, - //else return False -{ - if (gc.clippath==NULL) { - return false; - } - if (0 &(gc.clippath==lastclippath)) { - //std::cout << "seen it" << std::endl; - return true; - } - rendererBaseAlphaMask->clear(agg::gray8(0, 0)); - gc.clippath->rewind(0); - theRasterizer->add_path(*(gc.clippath)); - rendererAlphaMask->color(agg::gray8(255,255)); - agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); - lastclippath = gc.clippath; - return true; -} - -template<class PathSource> -void -RendererAgg::_render_lines_path(PathSource &path, const GCAgg& gc) { - _VERBOSE("RendererAgg::_render_lines_path"); - typedef PathSource path_t; - //typedef agg::conv_transform<agg::path_storage, agg::trans_affine> path_t; - typedef agg::conv_stroke<path_t> stroke_t; - typedef agg::conv_dash<path_t> dash_t; - - bool isclippath(gc.clippath!=NULL); - - if (gc.dasha==NULL ) { //no dashes - stroke_t stroke(path); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } - else { - dash_t dash(path); - - //todo: dash.dash_start(gc.dashOffset); - for (size_t i=0; i<gc.Ndash/2; i+=1) - dash.add_dash(gc.dasha[2*i], gc.dasha[2*i+1]); - - agg::conv_stroke<dash_t> stroke(dash); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.width(gc.linewidth); - theRasterizer->add_path(stroke); //boyle freeze is herre - } - - - if ( gc.isaa ) { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_aa_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - //std::cout << "render clippath" << std::endl; - - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); - } - } - else { - if (isclippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - typedef agg::renderer_scanline_bin_solid<amask_ren_type> renderer_type; - renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, ren); - } - else{ - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); - } - } -} - -Py::Object RendererAgg::draw_markers(const Py::Tuple& args) { typedef agg::conv_transform<agg::path_storage> transformed_path_t; typedef agg::conv_curve<transformed_path_t> curve_t; @@ -1284,10 +548,10 @@ if (!PathAgg::check(marker_path_obj)) throw Py::TypeError("Native path object is not of correct type"); PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr()); - agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]); + agg::trans_affine marker_trans = py_to_agg_transformation_matrix(args[2]); Py::Object vertices_obj = args[3]; Py::Object codes_obj = args[4]; - agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[5]); facepair_t face = _get_rgba_face(args[6], gc.alpha); // Deal with the difference in y-axis direction @@ -1337,7 +601,8 @@ unsigned strokeSize = scanlines.byte_size(); strokeCache = new agg::int8u[strokeSize]; // or any container scanlines.serialize(strokeCache); - + + // MGDTODO: Clean this up and support clippaths as well theRasterizer->reset_clipping(); if (gc.cliprect==NULL) { rendererBase->reset_clipping(true); @@ -1557,14 +822,20 @@ Py::Object RendererAgg::convert_to_native_path(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); - args.verify_length(2); + args.verify_length(1); - Py::Object vertices_obj = args[0]; - Py::Object codes_obj = args[1]; + Py::Object path = args[0]; + + return Py::asObject(new PathAgg(path)); +} + +PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) { + Py::Object vertices_obj = path_obj.getAttr("vertices"); + Py::Object codes_obj = path_obj.getAttr("codes"); + PyArrayObject* vertices = NULL; PyArrayObject* codes = NULL; - PathAgg* path = NULL; try { vertices = (PyArrayObject*)PyArray_ContiguousFromObject @@ -1576,8 +847,6 @@ if (!codes) throw Py::ValueError("Invalid codes array."); - path = new PathAgg(); - size_t next_vertex_stride = vertices->strides[0]; size_t next_axis_stride = vertices->strides[1]; size_t code_stride = codes->strides[0]; @@ -1593,31 +862,31 @@ switch (*(unsigned char*)(code_i)) { case MOVETO: GET_NEXT_VERTEX(x0, y0); - path->move_to(x0, y0); + move_to(x0, y0); _VERBOSE("MOVETO"); break; case LINETO: GET_NEXT_VERTEX(x0, y0); - path->line_to(x0, y0); + line_to(x0, y0); _VERBOSE("LINETO"); break; case CURVE3: GET_NEXT_VERTEX(x0, y0); GET_NEXT_VERTEX(x1, y1); - path->curve3(x0, y0, x1, y1); - path->curvy = true; + curve3(x0, y0, x1, y1); + curvy = true; _VERBOSE("CURVE3"); break; case CURVE4: GET_NEXT_VERTEX(x0, y0); GET_NEXT_VERTEX(x1, y1); GET_NEXT_VERTEX(x2, y2); - path->curve4(x0, y0, x1, y1, x2, y2); - path->curvy = true; + curve4(x0, y0, x1, y1, x2, y2); + curvy = true; _VERBOSE("CURVE4"); break; case CLOSEPOLY: - path->close_polygon(); + close_polygon(); _VERBOSE("CLOSEPOLY"); break; } @@ -1626,14 +895,11 @@ } catch(...) { Py_XDECREF(vertices); Py_XDECREF(codes); - delete path; throw; } Py_XDECREF(vertices); Py_XDECREF(codes); - - return Py::asObject(path); } Py::Object @@ -1643,7 +909,11 @@ typedef agg::conv_stroke<curve_t> stroke_t; typedef agg::conv_dash<curve_t> dash_t; typedef agg::conv_stroke<dash_t> stroke_dash_t; - //draw_path(gc, rgbFace, path, transform) + typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; + typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; + typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; + typedef agg::renderer_scanline_bin_solid<amask_ren_type> amask_bin_renderer_type; + theRasterizer->reset_clipping(); _VERBOSE("RendererAgg::draw_path"); @@ -1654,52 +924,118 @@ if (!PathAgg::check(path_obj)) throw Py::TypeError("Native path object is not of correct type"); PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); - agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[2]); + agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]); facepair_t face = _get_rgba_face(args[3], gc.alpha); trans *= agg::trans_affine_scaling(1.0, -1.0); trans *= agg::trans_affine_translation(0.0, (double)height); - transformed_path_t tpath(*path, trans); - // MGDTODO: See if there is any advantage to only curving if necessary - curve_t curve(tpath); + transformed_path_t* tpath = NULL; + agg::path_storage new_path; - set_clipbox_rasterizer(gc.cliprect); - - if (face.first) { - rendererAA->color(face.second); - theRasterizer->add_path(curve); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + bool has_clippath = (gc.clippath != NULL); + + if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) { + rendererBaseAlphaMask->clear(agg::gray8(0, 0)); + gc.clippath->rewind(0); + transformed_path_t transformed_clippath(*(gc.clippath), trans); + theRasterizer->add_path(transformed_clippath); + rendererAlphaMask->color(agg::gray8(255, 255)); + agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask); + lastclippath = gc.clippath; + lastclippath_transform = trans; } - if (gc.linewidth) { - if (gc.dasha == NULL) { - stroke_t stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->add_path(stroke); - } else { - dash_t dash(curve); - for (size_t i = 0; i < (gc.Ndash / 2); ++i) - dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]); - stroke_dash_t stroke(dash); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.width(gc.linewidth); - theRasterizer->add_path(stroke); //boyle freeze is herre + try { + // If this is a straight horizontal or vertical line, quantize to nearest + // pixels + if (path->total_vertices() == 2) { + double x0, y0, x1, y1; + path->vertex(0, &x0, &y0); + trans.transform(&x0, &y0); + path->vertex(1, &x1, &y1); + trans.transform(&x1, &y1); + if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) { + new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5); + new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5); + tpath = new transformed_path_t(new_path, agg::trans_affine()); + } } + + if (!tpath) { + tpath = new transformed_path_t(*path, trans); + } + + // Benchmarking shows that there is no noticable slowdown to always + // treating paths as having curved segments. Doing so greatly + // simplifies the code + curve_t curve(*tpath); - if ( gc.isaa ) { - rendererAA->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + set_clipbox_rasterizer(gc.cliprect); + + if (face.first) { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else{ + rendererAA->color(face.second); + theRasterizer->add_path(curve); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } } - else { - rendererBin->color(gc.color); - agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + + if (gc.linewidth) { + if (gc.dasha == NULL) { + stroke_t stroke(curve); + stroke.width(gc.linewidth); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + theRasterizer->add_path(stroke); + } else { + dash_t dash(curve); + for (size_t i = 0; i < (gc.Ndash / 2); ++i) + dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]); + stroke_dash_t stroke(dash); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + stroke.width(gc.linewidth); + theRasterizer->add_path(stroke); + } + + if (gc.isaa) { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererAA->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } + } else { + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_bin_renderer_type ren(r); + ren.color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, ren); + } else { + rendererBin->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + } + } } + } catch (...) { + delete tpath; + throw; } + delete tpath; + return Py::Object(); } @@ -2033,18 +1369,10 @@ behaviors().name("RendererAgg"); behaviors().doc("The agg backend extension module"); - add_varargs_method("draw_rectangle", &RendererAgg::draw_rectangle, - "draw_rectangle(gc, rgbFace, l, b, w, h)\n"); - add_varargs_method("draw_ellipse", &RendererAgg::draw_ellipse, - "draw_ellipse(gc, rgbFace, x, y, w, h)\n"); - add_varargs_method("draw_polygon", &RendererAgg::draw_polygon, - "draw_polygon(gc, rgbFace, points)\n"); add_varargs_method("draw_path",... [truncated message content] |
From: <md...@us...> - 2007-09-17 13:41:58
|
Revision: 3854 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3854&view=rev Author: mdboom Date: 2007-09-17 06:41:38 -0700 (Mon, 17 Sep 2007) Log Message: ----------- Transferring work-in-progress Modified Paths: -------------- branches/transforms/examples/shared_axis_demo.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Modified: branches/transforms/examples/shared_axis_demo.py =================================================================== --- branches/transforms/examples/shared_axis_demo.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/examples/shared_axis_demo.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -36,12 +36,12 @@ s2 = exp(-t) s3 = sin(4*pi*t) ax1 = subplot(311) -plot(t,s1) +plot(t,s1, "bH") setp( ax1.get_xticklabels(), fontsize=6) ## share x only ax2 = subplot(312, sharex=ax1) -plot(t, s2) +plot(t, s2, "b<") # make these tick labels invisible setp( ax2.get_xticklabels(), visible=False) Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -36,21 +36,25 @@ """ pass + def _get_cached_native_path(self, path): + native_path = self._native_paths.get(path) + if native_path is None: + import matplotlib.patches + print "CACHE MISS", path + native_path = self.convert_to_native_path(path) + self._native_paths[path] = native_path + return native_path + def draw_path(self, gc, path, transform, rgbFace = None): """ Handles the caching of the native path associated with the given path and calls the underlying backend's _draw_path to actually do the drawing. """ - native_path = self._native_paths.get(path) - if native_path is None: - import matplotlib.patches - print "CACHE MISS", path - native_path = self.convert_to_native_path(path) - self._native_paths[path] = native_path - self._draw_path(gc, native_path, transform, rgbFace) + native_path = self._get_cached_native_path(path) + self._draw_native_path(gc, native_path, transform, rgbFace) - def _draw_path(self, gc, native_path, transform, rgbFace): + def _draw_native_path(self, gc, native_path, transform, rgbFace): """ Draw the native path object with the given GraphicsContext and transform. The transform passed in will always be affine. @@ -107,9 +111,10 @@ return False def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - pass - - def _draw_markers(self, bgc, path, rgbFace, x, y, trans): + native_marker_path = self._get_cached_native_path(marker_path) + self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace) + + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): """ This method is currently underscore hidden because the draw_markers method is being used as a sentinel for newstyle @@ -130,7 +135,7 @@ vec6 = transform.as_vec6_val() ...backend dependent affine... """ - pass + raise NotImplementedError def draw_line_collection(self, segments, transform, clipbox, colors, linewidths, linestyle, antialiaseds, Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -140,7 +140,7 @@ def convert_to_native_path(self, path): return self._renderer.convert_to_native_path(path.vertices, path.codes) - def _draw_path(self, gc, native_path, transform, rgbFace): + def _draw_native_path(self, gc, native_path, transform, rgbFace): return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace) def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): @@ -172,8 +172,12 @@ def draw_lines(self, gc, x, y, transform): return self._renderer.draw_lines(gc, x, y, transform.to_values()) - def draw_markers(self, gc, path, color, x, y, transform): - return self._renderer.draw_markers(gc, path, color, x, y, transform.to_values()) + def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None): + return self._renderer.draw_markers( + gc, + native_marker_path, marker_trans.to_values(), + path.vertices, path.codes, trans.to_values(), + rgbFace) def draw_polygon(self, *args): return self._renderer.draw_polygon(*args) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -520,7 +520,7 @@ lineFunc(renderer, gc, self._path) # MGDTODO: Deal with markers - if self._marker is not None and False: + if self._marker is not None: gc = renderer.new_gc() self._set_gc_clip(gc) gc.set_foreground(self.get_markeredgecolor()) @@ -713,6 +713,9 @@ pass def _draw_steps(self, renderer, gc, xt, yt): + # MGDTODO: This is a quirky one. The step-plotting part + # should probably be moved to where the path is generated + # in recache, and then just drawn with _draw_solid siz=len(xt) if siz<2: return xt2=npy.ones((2*siz,), xt.dtype) @@ -727,323 +730,132 @@ renderer.draw_lines(gc, xt2, yt2) def _draw_solid(self, renderer, gc, path): - # if len(xt)<2: return gc.set_linestyle('solid') renderer.draw_path(gc, path, self.get_transform()) - def _draw_dashed(self, renderer, gc, xt, yt): - if len(xt)<2: return + def _draw_dashed(self, renderer, gc, path): gc.set_linestyle('dashed') if self._dashSeq is not None: gc.set_dashes(0, self._dashSeq) - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_dash_dot(self, renderer, gc, xt, yt): - if len(xt)<2: return + def _draw_dash_dot(self, renderer, gc, path): gc.set_linestyle('dashdot') - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_dotted(self, renderer, gc, xt, yt): - - if len(xt)<2: return + + def _draw_dotted(self, renderer, gc, path): gc.set_linestyle('dotted') - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) - def _draw_point(self, renderer, gc, xt, yt): + + def _draw_point(self, renderer, gc, path): + self._draw_circle(renderer, gc, path, point = True) - r = 0.5 * renderer.points_to_pixels(self._markersize) - r *= self._point_size_reduction - gc.set_linewidth(0) - if r <= 0.5: - self._draw_pixel(renderer, gc, xt, yt) - elif r <= 2: - self._draw_hexagon1(renderer, gc, xt, yt, point=True) - else: - self._draw_circle(renderer, gc, xt, yt, point=True) - - def _draw_pixel(self, renderer, gc, xt, yt): - if self._newstyle: - rgbFace = self._get_rgb_face() - path = agg.path_storage() - path.move_to(-0.5, -0.5) - path.line_to(-0.5, 0.5) - path.line_to(0.5, 0.5) - path.line_to(0.5, -0.5) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_point(gc, x, y) - - - def _draw_circle(self, renderer, gc, xt, yt, point=False): - + def _draw_pixel(self, renderer, gc, path): + rgbFace = self._get_rgb_face() + transform = Affine2D().translate(-0.5, -0.5) + renderer.draw_markers(gc, Path.unit_rectangle, transform, + path, self.get_transform(), rgbFace) + + + def _draw_circle(self, renderer, gc, path, point=False): w = renderer.points_to_pixels(self._markersize) if point: w *= self._point_size_reduction + w *= 0.5 - rgbFace = self._get_rgb_face() + transform = Affine2D().scale(w, w) + renderer.draw_markers( + gc, Path.unit_circle(), transform, path, self.get_transform(), + rgbFace) - if self._newstyle: - N = 50.0 - r = w/2. - rads = (2*math.pi/N)*npy.arange(N) - xs = r*npy.cos(rads) - ys = r*npy.sin(rads) - # todo: use curve3! - path = agg.path_storage() - path.move_to(xs[0], ys[0]) - for x, y in zip(xs[1:], ys[1:]): - path.line_to(x, y) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt,yt): - renderer.draw_arc(gc, rgbFace, - x, y, w, w, 0.0, 360.0, 0.0) - - - - def _draw_triangle_up(self, renderer, gc, xt, yt): - - + _triangle_path = Path([[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0]]) + def _draw_triangle_up(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offset, -offset) - path.line_to(offset, -offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offset, y-offset), - (x+offset, y-offset) ) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_triangle_down(self, renderer, gc, xt, yt): + def _draw_triangle_down(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, -offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - - path = agg.path_storage() - path.move_to(-offset, offset) - path.line_to(offset, offset) - path.line_to(0, -offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x-offset, y+offset), - (x+offset, y+offset), - (x, y-offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_triangle_left(self, renderer, gc, xt, yt): + + def _draw_triangle_left(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset).rotate_deg(90) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0) - path.line_to(offset, -offset) - path.line_to(offset, offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x-offset, y), - (x+offset, y-offset), - (x+offset, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - - def _draw_triangle_right(self, renderer, gc, xt, yt): + def _draw_triangle_right(self, renderer, gc, path): offset = 0.5*renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset, offset).rotate_deg(-90) rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(-offset, -offset) - path.line_to(-offset, offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x-offset, y-offset), - (x-offset, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) + renderer.draw_markers(gc, self._triangle_path, transform, + path, self.get_transform(), rgbFace) - - def _draw_square(self, renderer, gc, xt, yt): + def _draw_square(self, renderer, gc, path): side = renderer.points_to_pixels(self._markersize) - offset = side*0.5 + transform = Affine2D().translate(-0.5, -0.5).scale(side) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - - path = agg.path_storage() - path.move_to(-offset, -offset) - path.line_to(-offset, offset) - path.line_to(offset, offset) - path.line_to(offset, -offset) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - - for (x,y) in zip(xt, yt): - renderer.draw_rectangle( - gc, rgbFace, - x-offset, y-offset, side, side) - - def _draw_diamond(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) + + def _draw_diamond(self, renderer, gc, path): + side = renderer.points_to_pixels(self._markersize) + transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(side) rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(0, -offset) - path.line_to(-offset, 0) - path.line_to(0, offset) - path.end_poly() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - - - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x, y-offset), - (x-offset, y), - (x, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_thin_diamond(self, renderer, gc, xt, yt): - offset = 0.7*renderer.points_to_pixels(self._markersize) - xoffset = 0.6*offset + + def _draw_thin_diamond(self, renderer, gc, path): + offset = renderer.points_to_pixels(self._markersize) + transform = Affine2D().translate(0.5, 0.5).rotate_deg(45).scale(offset * 0.8, offset) rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_rectangle(), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(xoffset, 0) - path.line_to(0, -offset) - path.line_to(-xoffset, 0) - path.line_to(0, offset) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+xoffset, y), - (x, y-offset), - (x-xoffset, y), - (x, y+offset)) - renderer.draw_polygon(gc, rgbFace, verts) + + def _draw_pentagon(self, renderer, gc, path): + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(5), transform, + path, self.get_transform(), rgbFace) - def _draw_pentagon(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) - offsetX1 = offset*0.95 - offsetY1 = offset*0.31 - offsetX2 = offset*0.59 - offsetY2 = offset*0.81 - rgbFace = self._get_rgb_face() + + def _draw_hexagon1(self, renderer, gc, path, point=False): + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, + path, self.get_transform(), rgbFace) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offsetX2, -offsetY2) - path.line_to(+offsetX2, -offsetY2) - path.line_to(+offsetX1, offsetY1) - path.end_poly() - - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offsetX1, y+offsetY1), - (x-offsetX2, y-offsetY2), - (x+offsetX2, y-offsetY2), - (x+offsetX1, y+offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - - def _draw_hexagon1(self, renderer, gc, xt, yt, point=False): - offset = 0.6*renderer.points_to_pixels(self._markersize) - if point: - offset *= self._point_size_reduction - offsetX1 = offset*0.87 - offsetY1 = offset*0.5 - rgbFace = self._get_rgb_face() - - if self._newstyle: - path = agg.path_storage() - path.move_to(0, offset) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offsetX1, -offsetY1) - path.line_to(0, -offset) - path.line_to(offsetX1, -offsetY1) - path.line_to(offsetX1, offsetY1) - path.end_poly() - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x, y+offset), - (x-offsetX1, y+offsetY1), - (x-offsetX1, y-offsetY1), - (x, y-offset), - (x+offsetX1, y-offsetY1), - (x+offsetX1, y+offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - + def _draw_hexagon2(self, renderer, gc, xt, yt): - offset = 0.6*renderer.points_to_pixels(self._markersize) - offsetX1 = offset*0.5 - offsetY1 = offset*0.87 - rgbFace = self._get_rgb_face() - if self._newstyle: - path = agg.path_storage() - path.move_to(offset, 0) - path.line_to(offsetX1, offsetY1) - path.line_to(-offsetX1, offsetY1) - path.line_to(-offset, 0) - path.line_to(-offsetX1, -offsetY1) - path.line_to(offsetX1, -offsetY1) - path.end_poly() + offset = 0.5 * renderer.points_to_pixels(self._markersize) + transform = Affine2D().scale(offset).rotate_deg(30) + rgbFace = self._get_rgb_face() + renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, + path, self.get_transform(), rgbFace) - renderer.draw_markers(gc, path, rgbFace, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - verts = ( (x+offset, y), - (x+offsetX1, y+offsetY1), - (x-offsetX1, y+offsetY1), - (x-offset, y), - (x-offsetX1, y-offsetY1), - (x+offsetX1, y-offsetY1)) - renderer.draw_polygon(gc, rgbFace, verts) - + def _draw_vline(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) if self._newstyle: @@ -1055,6 +867,7 @@ for (x,y) in zip(xt, yt): renderer.draw_line(gc, x, y-offset, x, y+offset) + def _draw_hline(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) if self._newstyle: @@ -1066,46 +879,31 @@ for (x,y) in zip(xt, yt): renderer.draw_line(gc, x-offset, y, x+offset, y) - def _draw_tickleft(self, renderer, gc, xt, yt): + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) + def _draw_tickleft(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-offset, 0.5) - path.line_to(0, 0.5) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x-offset, y, x, y) + marker_transform = Affine2D().scale(offset, 1.0) + renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, + path, self.get_transform()) - def _draw_tickright(self, renderer, gc, xt, yt): - + def _draw_tickright(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(0, 0.5) - path.line_to(offset, 0.5) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x+offset, y) + marker_transform = Affine2D().scale(-offset, 1.0) + renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, + path, self.get_transform()) - _tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) - def _draw_tickup(self, renderer, gc, xt, yt): + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) + def _draw_tickup(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, offset) - renderer.draw_markers(gc, self._tickup_path, marker_transform, - self._path, self.get_transform()) + renderer.draw_markers(gc, self._tickvert_path, marker_transform, + path, self.get_transform()) - def _draw_tickdown(self, renderer, gc, xt, yt): + def _draw_tickdown(self, renderer, gc, path): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-0.5, -offset) - path.line_to(-0.5, 0) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y-offset, x, y) + marker_transform = Affine2D().scale(1.0, -offset) + renderer.draw_markers(gc, self._tickvert_path, marker_transform, + path, self.get_transform()) def _draw_plus(self, renderer, gc, xt, yt): offset = 0.5*renderer.points_to_pixels(self._markersize) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -288,7 +288,6 @@ self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - def _update(self): self.update_from(self.patch) if self.props is not None: @@ -316,8 +315,7 @@ """ - _path = Path( - [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + _path = Path.unit_rectangle() def __str__(self): return str(self.__class__).split('.')[-1] \ @@ -424,8 +422,6 @@ """ A regular polygon patch. """ - _polygon_cache = {} - def __str__(self): return "Poly%d(%g,%g)"%(self.numVertices,self.xy[0],self.xy[1]) @@ -442,14 +438,7 @@ """ Patch.__init__(self, **kwargs) - path = self._polygon_cache[numVertices] - if path is None: - theta = 2*npy.pi/numVertices * npy.arange(numVertices) - verts = npy.hstack((npy.cos(theta), npy.sin(theta))) - path = Path(verts) - self._polygon_cache[numVertices] = path - - self._path = path + self._path = Path.unit_regular_polygon(numVertices) self._poly_transform = transforms.Affine2D() \ .scale(radius) \ .rotate(orientation) \ Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/lib/matplotlib/path.py 2007-09-17 13:41:38 UTC (rev 3854) @@ -1,21 +1,25 @@ import numpy as npy -class Path: +VALIDATE_PATHS = True + +class Path(object): # Path codes STOP = 0 MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices + CLOSEPOLY = 5 ### # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, # so if they don't, we probably shouldn't - CURVEN = 5 - CATROM = 6 - UBSPLINE = 7 + CURVEN = 6 + CATROM = 7 + UBSPLINE = 8 #### - CLOSEPOLY = 0x0F # 0 vertices + NUM_VERTICES = [0, 1, 1, 2, 3, 0] + code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): @@ -38,9 +42,14 @@ self._codes = codes assert self._codes.ndim == 1 - # MGDTODO: Maybe we should add some quick-and-dirty check that - # the number of vertices is correct for the code array + if VALIDATE_PATHS: + i = 0 + NUM_VERTICES = self.NUM_VERTICES + for code in codes: + i += NUM_VERTICES[code] + assert i == len(self.vertices) + def _get_codes(self): return self._codes codes = property(_get_codes) @@ -48,3 +57,74 @@ def _get_vertices(self): return self._vertices vertices = property(_get_vertices) + + def iter_endpoints(self): + i = 0 + NUM_VERTICES = self.NUM_VERTICES + vertices = self.vertices + for code in self.codes: + num_vertices = NUM_VERTICES[code] + if num_vertices >= 1: + i += num_vertices - 1 + yield vertices[i] + i += 1 + + _unit_rectangle = None + #@classmethod + def unit_rectangle(cls): + if cls._unit_rectangle is None: + cls._unit_rectangle = \ + Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + return cls._unit_rectangle + unit_rectangle = classmethod(unit_rectangle) + + _unit_regular_polygons = {} + #@classmethod + def unit_regular_polygon(cls, numVertices): + path = cls._unit_regular_polygons.get(numVertices) + if path is None: + theta = 2*npy.pi/numVertices * npy.arange(numVertices) + # This is to make sure the polygon always "points-up" + theta += npy.pi / 2.0 + verts = npy.vstack((npy.cos(theta), npy.sin(theta))).transpose() + path = Path(verts) + cls._unit_regular_polygons[numVertices] = path + return path + unit_regular_polygon = classmethod(unit_regular_polygon) + + _unit_circle = None + #@classmethod + def unit_circle(cls): + # MGDTODO: Optimize? + if cls._unit_circle is None: + offset = 4.0 * (npy.sqrt(2) - 1) / 3.0 + vertices = npy.array( + [[-1.0, 0.0], + + [-1.0, offset], + [-offset, 1.0], + [0.0, 1.0], + + [offset, 1.0], + [1.0, offset], + [1.0, 0.0], + + [1.0, -offset], + [offset, -1.0], + [0.0, -1.0], + + [-offset, -1.0], + [-1.0, -offset], + [-1.0, 0.0]], + npy.float_) + codes = npy.array( + [cls.MOVETO, + cls.CURVE4, + cls.CURVE4, + cls.CURVE4, + cls.CURVE4, + cls.CLOSEPOLY], + cls.code_type) + cls._unit_circle = Path(vertices, codes) + return cls._unit_circle + unit_circle = classmethod(unit_circle) Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/src/_backend_agg.cpp 2007-09-17 13:41:38 UTC (rev 3854) @@ -64,6 +64,20 @@ Py::Float(seq[5])); } +// MGDTODO: Implement this as a nice iterator +inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, + double& x, double& y, + size_t next_vertex_stride, + size_t next_axis_stride) { + if (vertex_i + next_axis_stride >= vertex_end) + throw Py::ValueError("Error parsing path. Read past end of vertices"); + x = *(double*)vertex_i; + y = *(double*)(vertex_i + next_axis_stride); + vertex_i += next_vertex_stride; +} + +#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) + GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) : dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0), cliprect(NULL), clippath(NULL), @@ -1255,146 +1269,134 @@ Py::Object RendererAgg::draw_markers(const Py::Tuple& args) { + typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_curve<transformed_path_t> curve_t; + typedef agg::conv_stroke<curve_t> stroke_t; + typedef agg::conv_dash<curve_t> dash_t; + typedef agg::conv_stroke<dash_t> stroke_dash_t; + theRasterizer->reset_clipping(); - _VERBOSE("RendererAgg::_draw_markers_cache"); - args.verify_length(6); + args.verify_length(7); - _VERBOSE("RendererAgg::_draw_markers_cache setting gc"); GCAgg gc = GCAgg(args[0], dpi); + Py::Object marker_path_obj = args[1]; + if (!PathAgg::check(marker_path_obj)) + throw Py::TypeError("Native path object is not of correct type"); + PathAgg* marker_path = static_cast<PathAgg*>(marker_path_obj.ptr()); + agg::trans_affine marker_trans = py_sequence_to_agg_transformation_matrix(args[2]); + Py::Object vertices_obj = args[3]; + Py::Object codes_obj = args[4]; + agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[5]); + facepair_t face = _get_rgba_face(args[6], gc.alpha); + + // Deal with the difference in y-axis direction + marker_trans *= agg::trans_affine_scaling(1.0, -1.0); + trans *= agg::trans_affine_scaling(1.0, -1.0); + trans *= agg::trans_affine_translation(0.0, (double)height); + marker_path->rewind(0); + transformed_path_t marker_path_transformed(*marker_path, marker_trans); + curve_t marker_path_curve(marker_path_transformed); - agg::path_storage *ppath; - - swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *"); - assert(descr); - if (SWIG_ConvertPtr(args[1].ptr(),(void **)(&ppath), descr, 0) == -1) { - throw Py::TypeError("Could not convert path_storage"); - } - facepair_t face = _get_rgba_face(args[2], gc.alpha); - - Py::Object xo = args[3]; - Py::Object yo = args[4]; - - PyArrayObject *xa = (PyArrayObject *) PyArray_ContiguousFromObject(xo.ptr(), PyArray_DOUBLE, 1, 1); - - if (xa==NULL) - throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array"); - - - PyArrayObject *ya = (PyArrayObject *) PyArray_ContiguousFromObject(yo.ptr(), PyArray_DOUBLE, 1, 1); - - if (ya==NULL) - throw Py::TypeError("RendererAgg::_draw_markers_cache expected numerix array"); - - agg::trans_affine xytrans = py_sequence_to_agg_transformation_matrix(args[5]); - - size_t Nx = xa->dimensions[0]; - size_t Ny = ya->dimensions[0]; - - if (Nx!=Ny) - throw Py::ValueError(Printf("x and y must be equal length arrays; found %d and %d", Nx, Ny).str()); - - - double heightd = double(height); - - - ppath->rewind(0); - ppath->flip_y(0,0); - typedef agg::conv_curve<agg::path_storage> curve_t; - curve_t curve(*ppath); - //maxim's suggestions for cached scanlines agg::scanline_storage_aa8 scanlines; theRasterizer->reset(); agg::int8u* fillCache = NULL; - unsigned fillSize = 0; - if (face.first) { - theRasterizer->add_path(curve); + agg::int8u* strokeCache = NULL; + PyArrayObject* vertices = NULL; + PyArrayObject* codes = NULL; + + try { + vertices = (PyArrayObject*)PyArray_ContiguousFromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) + throw Py::ValueError("Invalid vertices array."); + codes = (PyArrayObject*)PyArray_ContiguousFromObject + (codes_obj.ptr(), PyArray_UINT8, 1, 1); + if (!codes) + throw Py::ValueError("Invalid codes array."); + + unsigned fillSize = 0; + if (face.first) { + theRasterizer->add_path(marker_path_curve); + agg::render_scanlines(*theRasterizer, *slineP8, scanlines); + fillSize = scanlines.byte_size(); + fillCache = new agg::int8u[fillSize]; // or any container + scanlines.serialize(fillCache); + } + + stroke_t stroke(marker_path_curve); + stroke.width(gc.linewidth); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + theRasterizer->reset(); + theRasterizer->add_path(stroke); agg::render_scanlines(*theRasterizer, *slineP8, scanlines); - fillSize = scanlines.byte_size(); - fillCache = new agg::int8u[fillSize]; // or any container - scanlines.serialize(fillCache); - } + unsigned strokeSize = scanlines.byte_size(); + strokeCache = new agg::int8u[strokeSize]; // or any container + scanlines.serialize(strokeCache); - agg::conv_stroke<curve_t> stroke(curve); - stroke.width(gc.linewidth); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - theRasterizer->reset(); - theRasterizer->add_path(stroke); - agg::render_scanlines(*theRasterizer, *slineP8, scanlines); - unsigned strokeSize = scanlines.byte_size(); - agg::int8u* strokeCache = new agg::int8u[strokeSize]; // or any container - scanlines.serialize(strokeCache); - - theRasterizer->reset_clipping(); - - - if (gc.cliprect==NULL) { - rendererBase->reset_clipping(true); - } - else { - int l = (int)(gc.cliprect[0]) ; - int b = (int)(gc.cliprect[1]) ; - int w = (int)(gc.cliprect[2]) ; - int h = (int)(gc.cliprect[3]) ; - rendererBase->clip_box(l, height-(b+h),l+w, height-b); - } - - - double thisx, thisy; - for (size_t i=0; i<Nx; i++) { - thisx = *(double *)(xa->data + i*xa->strides[0]); - thisy = *(double *)(ya->data + i*ya->strides[0]); - - // MGDTODO -// if (mpltransform->need_nonlinear_api()) -// try { -// mpltransform->nonlinear_only_api(&thisx, &thisy); -// } -// catch(...) { -// continue; -// } + theRasterizer->reset_clipping(); + if (gc.cliprect==NULL) { + rendererBase->reset_clipping(true); + } + else { + int l = (int)(gc.cliprect[0]) ; + int b = (int)(gc.cliprect[1]) ; + int w = (int)(gc.cliprect[2]) ; + int h = (int)(gc.cliprect[3]) ; + rendererBase->clip_box(l, height-(b+h),l+w, height-b); + } - xytrans.transform(&thisx, &thisy); - - thisy = heightd - thisy; //flipy - - thisx = (int)thisx + 0.5; - thisy = (int)thisy + 0.5; - if (thisx<0) continue; - if (thisy<0) continue; - if (thisx>width) continue; - if (thisy>height) continue; - + size_t next_vertex_stride = vertices->strides[0]; + size_t next_axis_stride = vertices->strides[1]; + size_t code_stride = codes->strides[0]; + + const char* vertex_i = vertices->data; + const char* code_i = codes->data; + const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]); + + size_t N = codes->dimensions[0]; + double x, y; + agg::serialized_scanlines_adaptor_aa8 sa; agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; - - if (face.first) { - //render the fill - sa.init(fillCache, fillSize, thisx, thisy); - rendererAA->color(face.second); - agg::render_scanlines(sa, sl, *rendererAA); + + for (size_t i=0; i < N; i++) { + size_t num_vertices = NUM_VERTICES[(int)(*code_i)]; + if (num_vertices) { + for (size_t j=0; j<num_vertices; ++j) + GET_NEXT_VERTEX(x, y); + trans.transform(&x, &y); + + if (face.first) { + //render the fill + sa.init(fillCache, fillSize, x, y); + rendererAA->color(face.second); + agg::render_scanlines(sa, sl, *rendererAA); + } + + //render the stroke + sa.init(strokeCache, strokeSize, x, y); + rendererAA->color(gc.color); + agg::render_scanlines(sa, sl, *rendererAA); + } + code_i += code_stride; } - - //render the stroke - sa.init(strokeCache, strokeSize, thisx, thisy); - rendererAA->color(gc.color); - agg::render_scanlines(sa, sl, *rendererAA); - - } //for each marker + } catch(...) { + Py_XDECREF(vertices); + Py_XDECREF(codes); + delete[] fillCache; + delete[] strokeCache; + } - Py_XDECREF(xa); - Py_XDECREF(ya); - - if (face.first) - delete [] fillCache; + Py_XDECREF(vertices); + Py_XDECREF(codes); + delete [] fillCache; delete [] strokeCache; - //jdh - _VERBOSE("RendererAgg::_draw_markers_cache done"); return Py::Object(); } @@ -1552,21 +1554,6 @@ } -inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, - double& x, double& y, - size_t next_vertex_stride, - size_t next_axis_stride) { - if (vertex_i + next_axis_stride >= vertex_end) - throw Py::ValueError("Error parsing path. Read past end of vertices"); - x = *(double*)vertex_i; - y = *(double*)(vertex_i + next_axis_stride); - vertex_i += next_vertex_stride; -} - -#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) - - - Py::Object RendererAgg::convert_to_native_path(const Py::Tuple& args) { _VERBOSE("RendererAgg::draw_image"); @@ -2059,7 +2046,7 @@ add_varargs_method("draw_lines", &RendererAgg::draw_lines, "draw_lines(gc, x, y,)\n"); add_varargs_method("draw_markers", &RendererAgg::draw_markers, - "draw_markers(gc, path, x, y)\n"); + "draw_markers(gc, marker_path, marker_trans, vertices, codes, rgbFace)\n"); add_varargs_method("draw_text_image", &RendererAgg::draw_text_image, "draw_text_image(font_image, x, y, r, g, b, a)\n"); add_varargs_method("draw_image", &RendererAgg::draw_image, Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-09-15 04:01:56 UTC (rev 3853) +++ branches/transforms/src/_backend_agg.h 2007-09-17 13:41:38 UTC (rev 3854) @@ -45,8 +45,10 @@ #define LINETO 2 #define CURVE3 3 #define CURVE4 4 -#define CLOSEPOLY 0x0F +#define CLOSEPOLY 5 +const size_t NUM_VERTICES[] = { 0, 1, 1, 2, 3, 0 }; + typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2007-09-15 04:01:58
|
Revision: 3853 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3853&view=rev Author: jouni Date: 2007-09-14 21:01:56 -0700 (Fri, 14 Sep 2007) Log Message: ----------- Bugfix and doc fixes in type1font.py Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/type1font.py Modified: trunk/matplotlib/lib/matplotlib/type1font.py =================================================================== --- trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-14 17:57:52 UTC (rev 3852) +++ trunk/matplotlib/lib/matplotlib/type1font.py 2007-09-15 04:01:56 UTC (rev 3853) @@ -1,15 +1,15 @@ """ A class representing a Type 1 font. -This version merely allows reading in pfa and pfb files, stores the -data in pfa format, and allows reading the parts of the data in a -format suitable for embedding in pdf files. A more complete class -might support subsetting. +This version merely reads pfa and pfb files and splits them for +embedding in pdf files. There is no support yet for subsetting or +anything like that. -Usage: font = Type1Font(filename) - somefile.write(font.data) # writes out font in pfa format - len1, len2, len3 = font.lengths() # needed for pdf embedding +Usage (subject to change): + font = Type1Font(filename) + clear_part, encrypted_part, finale = font.parts + Source: Adobe Technical Note #5040, Supporting Downloadable PostScript Language Fonts. @@ -32,8 +32,7 @@ def _read(self, file): rawdata = file.read() if not rawdata.startswith(chr(128)): - self.data = rawdata - return + return rawdata data = '' while len(rawdata) > 0: @@ -101,4 +100,6 @@ if __name__ == '__main__': import sys font = Type1Font(sys.argv[1]) - sys.stdout.write(font.data) + parts = font.parts + print len(parts[0]), len(parts[1]), len(parts[2]) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-14 18:01:00
|
Revision: 3852 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3852&view=rev Author: mdboom Date: 2007-09-14 10:57:52 -0700 (Fri, 14 Sep 2007) Log Message: ----------- Sends paths to backend only once, and after that uses the "native" path by reference with a changing transform. Started recongfiguring patches.py to use only Paths under the hood (to take advantage of this caching). Removed many methods from backend_agg that should eventually be replaced by draw_path, at least in theory. Modified Paths: -------------- branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h Added Paths: ----------- branches/transforms/lib/matplotlib/path.py Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -4,7 +4,7 @@ """ from __future__ import division -import os, sys, warnings, copy +import os, sys, warnings, copy, weakref import numpy as npy import matplotlib.numerix.npyma as ma @@ -17,7 +17,10 @@ class RendererBase: """An abstract base class to handle drawing/rendering operations """ - + # This will cache paths across rendering instances + # Each subclass of RenderBase should define this --> + # _paths = weakref.WeakKeyDictionary() + def __init__(self): self._texmanager = None @@ -33,7 +36,35 @@ """ pass + def draw_path(self, gc, path, transform, rgbFace = None): + """ + Handles the caching of the native path associated with the + given path and calls the underlying backend's _draw_path to + actually do the drawing. + """ + native_path = self._native_paths.get(path) + if native_path is None: + import matplotlib.patches + print "CACHE MISS", path + native_path = self.convert_to_native_path(path) + self._native_paths[path] = native_path + self._draw_path(gc, native_path, transform, rgbFace) + def _draw_path(self, gc, native_path, transform, rgbFace): + """ + Draw the native path object with the given GraphicsContext and + transform. The transform passed in will always be affine. + """ + raise NotImplementedError + + def convert_to_native_path(self, path): + """ + Backends will normally will override this, but if they don't need any + special optimizations, they can just have the generic path data + passed to them in draw_path. + """ + return path + def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation): """ @@ -75,6 +106,9 @@ """ return False + def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): + pass + def _draw_markers(self, bgc, path, rgbFace, x, y, trans): """ This method is currently underscore hidden because the Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -69,7 +69,7 @@ """ from __future__ import division -import os, sys +import os, sys, weakref import numpy as npy @@ -95,7 +95,15 @@ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles """ - + # MGDTODO: Renderers seem to get created and destroyed fairly + # often so the paths are cached at the class (not instance) level. + # However, this dictionary is only directly used by RendererBase, + # so it seems funny to define it here. However, if we didn't, the + # native paths would be shared across renderers, which is + # obviously bad. Seems like a good use of metaclasses, but that + # also seems like a heavy solution for a minor problem. + _native_paths = weakref.WeakKeyDictionary() + debug=1 texd = {} # a cache of tex image rasters def __init__(self, width, height, dpi): @@ -129,6 +137,12 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') + def convert_to_native_path(self, path): + return self._renderer.convert_to_native_path(path.vertices, path.codes) + + def _draw_path(self, gc, native_path, transform, rgbFace): + return self._renderer.draw_path(gc, native_path, transform.to_values(), rgbFace) + def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation): """ Draw an arc centered at x,y with width and height and angles Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -10,14 +10,14 @@ import numpy as npy -import agg import numerix.ma as ma from matplotlib import verbose import artist from artist import Artist, setp from cbook import iterable, is_string_like, is_numlike from colors import colorConverter -from transforms import Bbox +from path import Path +from transforms import Affine2D, Bbox from matplotlib import rcParams @@ -284,9 +284,6 @@ self.set_data(xdata, ydata) self._logcache = None - # TODO: do we really need 'newstyle' - self._newstyle = False - def contains(self, mouseevent): """Test whether the mouse event occurred on the line. The pick radius determines the precision of the location test (usually within five points of the value). Use @@ -427,6 +424,7 @@ if len(x) != len(y): raise RuntimeError('xdata and ydata must be the same length') + # MGDTODO: Deal with segments mx = ma.getmask(x) my = ma.getmask(y) mask = ma.mask_or(mx, my) @@ -439,7 +437,9 @@ self._x = npy.asarray(x, float) self._y = npy.asarray(y, float) - + self._path = Path(npy.vstack((self._x, self._y)).transpose(), + closed=False) + self._logcache = None @@ -508,30 +508,19 @@ gc.set_joinstyle(join) gc.set_capstyle(cap) - if self._newstyle: - # transform in backend - xt = self._x - yt = self._y - else: - x, y = self._get_plottable() - if len(x)==0: return - xt, yt = self.get_transform().numerix_x_y(x, y) - - - 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, xt, yt) - - - if self._marker is not None: - + lineFunc(renderer, gc, self._path) + + # MGDTODO: Deal with markers + if self._marker is not None and False: gc = renderer.new_gc() self._set_gc_clip(gc) gc.set_foreground(self.get_markeredgecolor()) @@ -539,7 +528,7 @@ gc.set_alpha(self._alpha) funcname = self._markers.get(self._marker, '_draw_nothing') markerFunc = getattr(self, funcname) - markerFunc(renderer, gc, xt, yt) + markerFunc(renderer, gc, self._path) #renderer.close_group('line2d') @@ -720,7 +709,7 @@ self.set_linestyle('--') self._dashSeq = seq # TODO: offset ignored for now - def _draw_nothing(self, renderer, gc, xt, yt): + def _draw_nothing(self, renderer, gc, path): pass def _draw_steps(self, renderer, gc, xt, yt): @@ -737,13 +726,10 @@ else: renderer.draw_lines(gc, xt2, yt2) - def _draw_solid(self, renderer, gc, xt, yt): - if len(xt)<2: return + def _draw_solid(self, renderer, gc, path): + # if len(xt)<2: return gc.set_linestyle('solid') - if self._newstyle: - renderer.draw_lines(gc, xt, yt, self.get_transform()) - else: - renderer.draw_lines(gc, xt, yt) + renderer.draw_path(gc, path, self.get_transform()) def _draw_dashed(self, renderer, gc, xt, yt): @@ -1103,16 +1089,12 @@ for (x,y) in zip(xt, yt): renderer.draw_line(gc, x, y, x+offset, y) + _tickup_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) def _draw_tickup(self, renderer, gc, xt, yt): offset = renderer.points_to_pixels(self._markersize) - if self._newstyle: - path = agg.path_storage() - path.move_to(-0.5, 0) - path.line_to(-0.5, offset) - renderer.draw_markers(gc, path, None, xt, yt, self.get_transform()) - else: - for (x,y) in zip(xt, yt): - renderer.draw_line(gc, x, y, x, y+offset) + marker_transform = Affine2D().scale(1.0, offset) + renderer.draw_markers(gc, self._tickup_path, marker_transform, + self._path, self.get_transform()) def _draw_tickdown(self, renderer, gc, xt, yt): offset = renderer.points_to_pixels(self._markersize) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -11,8 +11,8 @@ import matplotlib.nxutils as nxutils import matplotlib.mlab as mlab import matplotlib.artist as artist +from matplotlib.path import Path - # these are not available for the object inspector until after the # class is build so we define an initial set here for the init # function and they will be overridden after object defn @@ -73,7 +73,7 @@ self._antialiased = antialiased self._hatch = hatch self.fill = fill - + if len(kwargs): artist.setp(self, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -84,8 +84,10 @@ Returns T/F, {} """ - if callable(self._contains): return self._contains(self,mouseevent) + # MGDTODO: This will probably need to be implemented in C++ + if callable(self._contains): return self._contains(self,mouseevent) + try: # TODO: make this consistent with patch collection algorithm x, y = self.get_transform().inverse_xy_tup((mouseevent.x, mouseevent.y)) @@ -107,7 +109,6 @@ self.set_figure(other.get_figure()) self.set_alpha(other.get_alpha()) - def get_antialiased(self): return self._antialiased @@ -210,22 +211,16 @@ if self._hatch: gc.set_hatch(self._hatch ) - verts = self.get_verts() - tverts = self.get_transform()(verts) + path = self.get_path() + transform = self.get_transform() - # MGDTODO: This result is an Nx2 numpy array, which could be passed - # directly to renderer.draw_polygon since it currently expects - # a list of tuples so we're converting it to that now. - tverts = [tuple(x) for x in tverts] - - renderer.draw_polygon(gc, rgbFace, tverts) + renderer.draw_path(gc, path, transform, rgbFace) - #renderer.close_group('patch') - def get_verts(self): + def get_path(self): """ - Return the vertices of the patch + Return the path of this patch """ raise NotImplementedError('Derived must override') @@ -286,9 +281,10 @@ %(Patch)s """ Patch.__init__(self) - self.ox, self.oy = ox, oy self.patch = patch self.props = props + self.ox, self.oy = ox, oy + self._shadow_transform = transforms.Affine2D.translate(self.ox, self.oy) self._update() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd @@ -306,18 +302,13 @@ self.set_facecolor((r,g,b)) self.set_edgecolor((r,g,b)) + + def get_path(self): + return self.patch.get_path() - def get_verts(self): - verts = self.patch.get_verts() - xs = self.convert_xunits([x+self.ox for x,y in verts]) - ys = self.convert_yunits([y+self.oy for x,y in verts]) - return zip(xs, ys) - - def _draw(self, renderer): - 'draw the shadow' - self._update() - Patch.draw(self, renderer) - + def get_transform(self): + return self._transform + self._shadow_transform + class Rectangle(Patch): """ Draw a rectangle with lower left at xy=(x,y) with specified @@ -325,12 +316,16 @@ """ + _path = Path( + [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + def __str__(self): return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) - def __init__(self, xy, width, height, - **kwargs): + # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will + # happen automatically (without needing to call set_x etc. + def __init__(self, xy, width, height, **kwargs): """ xy is an x,y tuple lower, left @@ -344,38 +339,41 @@ Patch.__init__(self, **kwargs) - self.xy = list(xy) - self.width, self.height = width, height + self._bbox = transforms.Bbox.from_lbwh(xy[0], xy[1], width, height) + self._rect_transform = transforms.BboxTransform( + transforms.Bbox.unit(), self._bbox) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - - def get_verts(self): + def get_path(self): """ Return the vertices of the rectangle """ - x, y = self.xy - left, right = self.convert_xunits((x, x + self.width)) - bottom, top = self.convert_yunits((y, y + self.height)) + # This is a "class-static" variable, so all rectangles in the plot + # will be shared (and merely have different transforms) + return self._path - return npy.array([[left, bottom], [left, top], - [right, top], [right, bottom]], - npy.float_) + # MGDTODO: Convert units +# left, right = self.convert_xunits((x, x + self.width)) +# bottom, top = self.convert_yunits((y, y + self.height)) + def get_transform(self): + return self._rect_transform + self._transform + def get_x(self): "Return the left coord of the rectangle" - return self.xy[0] + return self._bbox.xmin def get_y(self): "Return the bottom coord of the rectangle" - return self.xy[1] + return self._bbox.ymin def get_width(self): "Return the width of the rectangle" - return self.width + return self._bbox.width def get_height(self): "Return the height of the rectangle" - return self.height + return self._bbox.height def set_x(self, x): """ @@ -383,7 +381,7 @@ ACCEPTS: float """ - self.xy[0] = x + self._bbox.xmin = x def set_y(self, y): """ @@ -391,7 +389,7 @@ ACCEPTS: float """ - self.xy[1] = y + self._bbox.ymin = y def set_width(self, w): """ @@ -399,7 +397,7 @@ ACCEPTS: float """ - self.width = w + self._bbox.width = w def set_height(self, h): """ @@ -407,7 +405,7 @@ ACCEPTS: float """ - self.height = h + self._bbox.height = h def set_bounds(self, *args): """ @@ -419,15 +417,15 @@ l,b,w,h = args[0] else: l,b,w,h = args - self.xy = [l,b] - self.width = w - self.height = h + self._bbox.bounds = l,b,w,h class RegularPolygon(Patch): """ A regular polygon patch. """ + _polygon_cache = {} + def __str__(self): return "Poly%d(%g,%g)"%(self.numVertices,self.xy[0],self.xy[1]) @@ -444,32 +442,27 @@ """ Patch.__init__(self, **kwargs) - self.xy = list(xy) - self.numVertices = numVertices - self.radius = radius - self.orientation = orientation + path = self._polygon_cache[numVertices] + if path is None: + theta = 2*npy.pi/numVertices * npy.arange(numVertices) + verts = npy.hstack((npy.cos(theta), npy.sin(theta))) + path = Path(verts) + self._polygon_cache[numVertices] = path + self._path = path + self._poly_transform = transforms.Affine2D() \ + .scale(radius) \ + .rotate(orientation) \ + .translate(*xy) + __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + def get_path(self): + return self._path - - def get_verts(self): - theta = 2*npy.pi/self.numVertices*npy.arange(self.numVertices) + \ - self.orientation - r = float(self.radius) - x, y = map(float, self.xy) - - xs = x + r*npy.cos(theta) - ys = y + r*npy.sin(theta) - - #xs = self.convert_xunits(xs) - #ys = self.convert_yunits(ys) - - - self.verts = zip(xs, ys) - - return self.verts - + def get_transform(self): + return self._poly_transform + self._transform + class Polygon(Patch): """ A general polygon patch. @@ -485,22 +478,20 @@ %(Patch)s See Patch documentation for additional kwargs """ - + # MGDTODO: This should encourage the use of numpy arrays of shape Nx2 Patch.__init__(self, **kwargs) if not isinstance(xy, list): xy = list(xy) - self.xy = xy + self._path = Path(xy, closed=False) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd - - def get_verts(self): - xs, ys = zip(*self.xy)[:2] + return self._path + + # MGDTODO: Convert units xs = self.convert_xunits(xs) ys = self.convert_yunits(ys) - return zip(xs, ys) - class Wedge(Polygon): def __str__(self): return "Wedge(%g,%g)"%self.xy[0] @@ -516,6 +507,7 @@ %(Patch)s """ + # MGDTODO: Implement me xc, yc = center rads = (math.pi/180.)*npy.arange(theta1, theta2+0.1*dtheta, dtheta) xs = r*npy.cos(rads)+xc @@ -543,6 +535,7 @@ Valid kwargs are: %(Patch)s """ + # MGDTODO: Implement me arrow = npy.array( [ [ 0.0, 0.1 ], [ 0.0, -0.1], [ 0.8, -0.1 ], [ 0.8, -0.3], @@ -586,6 +579,7 @@ %(Patch)s """ + # MGDTODO: Implement me if head_width is None: head_width = 3 * width if head_length is None: @@ -659,6 +653,7 @@ %(Patch)s """ + # MGDTODO: Implement me self.dpi = dpi self.xytip = xytip self.xybase = xybase @@ -731,6 +726,7 @@ %(Patch)s """ + # MGDTODO: Implement me self.center = xy self.radius = radius RegularPolygon.__init__(self, xy, @@ -767,6 +763,7 @@ Valid kwargs are: %(Patch)s """ + # MGDTODO: Implement me Patch.__init__(self, **kwargs) # self.center = npy.array(xy, npy.float) @@ -833,6 +830,7 @@ %(Patch)s """ + # MGDTODO: Implement me if kwargs.has_key('resolution'): import warnings warnings.warn('Circle is now scale free. Use CirclePolygon instead!', DeprecationWarning) Added: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py (rev 0) +++ branches/transforms/lib/matplotlib/path.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -0,0 +1,50 @@ +import numpy as npy + +class Path: + # Path codes + STOP = 0 + MOVETO = 1 # 1 vertex + LINETO = 2 # 1 vertex + CURVE3 = 3 # 2 vertices + CURVE4 = 4 # 3 vertices + ### + # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, + # so if they don't, we probably shouldn't + CURVEN = 5 + CATROM = 6 + UBSPLINE = 7 + #### + CLOSEPOLY = 0x0F # 0 vertices + + code_type = npy.uint8 + + def __init__(self, vertices, codes=None, closed=True): + self._vertices = npy.asarray(vertices, npy.float_) + assert self._vertices.ndim == 2 + assert self._vertices.shape[1] == 2 + + if codes is None: + if closed: + codes = self.LINETO * npy.ones( + self._vertices.shape[0] + 1, self.code_type) + codes[0] = self.MOVETO + codes[-1] = self.CLOSEPOLY + else: + codes = self.LINETO * npy.ones( + self._vertices.shape[0], self.code_type) + codes[0] = self.MOVETO + else: + codes = npy.asarray(codes, self.code_type) + self._codes = codes + + assert self._codes.ndim == 1 + # MGDTODO: Maybe we should add some quick-and-dirty check that + # the number of vertices is correct for the code array + + def _get_codes(self): + return self._codes + codes = property(_get_codes) + + def _get_vertices(self): + return self._vertices + vertices = property(_get_vertices) Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 17:57:52 UTC (rev 3852) @@ -403,6 +403,9 @@ Transform.__init__(self) self._inverted = None + def __array__(self): + return self.get_matrix() + def _do_invalidation(self): result = self._inverted is None self._inverted = None Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/src/_backend_agg.cpp 2007-09-14 17:57:52 UTC (rev 3852) @@ -341,6 +341,7 @@ } +// MGDTODO: Remove this method (it has been conglomerated into draw_path template <class VS> void RendererAgg::_fill_and_stroke(VS& path, @@ -1399,68 +1400,6 @@ } - - -// Py::Object -// RendererAgg::draw_path(const Py::Tuple& args) { -// //draw_path(gc, rgbFace, path, transform) -// theRasterizer->reset_clipping(); - -// _VERBOSE("RendererAgg::draw_path"); -// args.verify_length(4); - -// GCAgg gc = GCAgg(args[0], dpi); -// facepair_t face = _get_rgba_face(args[1], gc.alpha); - -// agg::path_storage *path; -// swig_type_info * descr = SWIG_TypeQuery("agg::path_storage *"); -// assert(descr); -// if (SWIG_ConvertPtr(args[2].ptr(),(void **)(&path), descr, 0) == -1) -// throw Py::TypeError("Could not convert path_storage"); - - -// Transformation* mpltransform = static_cast<Transformation*>(args[3].ptr()); - -// double a, b, c, d, tx, ty; -// try { -// mpltransform->affine_params_api(&a, &b, &c, &d, &tx, &ty); -// } -// catch(...) { -// throw Py::ValueError("Domain error on affine_params_api in RendererAgg::draw_path"); -// } - -// agg::trans_affine xytrans = agg::trans_affine(a,b,c,d,tx,ty); - -// double heightd = double(height); -// agg::path_storage tpath; // the mpl transformed path -// bool needNonlinear = mpltransform->need_nonlinear_api(); -// size_t Nx = path->total_vertices(); -// double x, y; -// unsigned cmd; -// bool curvy = false; -// for (size_t i=0; i<Nx; i++) { -// cmd = path->vertex(i, &x, &y); -// if (cmd==agg::path_cmd_curve3 || cmd==agg::path_cmd_curve4) curvy=true; -// if (needNonlinear) -// try { -// mpltransform->nonlinear_only_api(&x, &y); -// } -// catch (...) { -// throw Py::ValueError("Domain error on nonlinear_only_api in RendererAgg::draw_path"); - -// } - -// //use agg's transformer? -// xytrans.transform(&x, &y); -// y = heightd - y; //flipy -// tpath.add_vertex(x,y,cmd); -// } - -// _fill_and_stroke(tpath, gc, face, curvy); -// return Py::Object(); - -// } - /** * This is a custom span generator that converts spans in the * 8-bit inverted greyscale font buffer to rgba that agg can use. @@ -1613,8 +1552,172 @@ } +inline void get_next_vertex(const char* & vertex_i, const char* vertex_end, + double& x, double& y, + size_t next_vertex_stride, + size_t next_axis_stride) { + if (vertex_i + next_axis_stride >= vertex_end) + throw Py::ValueError("Error parsing path. Read past end of vertices"); + x = *(double*)vertex_i; + y = *(double*)(vertex_i + next_axis_stride); + vertex_i += next_vertex_stride; +} +#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride) + + + Py::Object +RendererAgg::convert_to_native_path(const Py::Tuple& args) { + _VERBOSE("RendererAgg::draw_image"); + args.verify_length(2); + + Py::Object vertices_obj = args[0]; + Py::Object codes_obj = args[1]; + + PyArrayObject* vertices = NULL; + PyArrayObject* codes = NULL; + PathAgg* path = NULL; + + try { + vertices = (PyArrayObject*)PyArray_ContiguousFromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2) + throw Py::ValueError("Invalid vertices array."); + codes = (PyArrayObject*)PyArray_ContiguousFromObject + (codes_obj.ptr(), PyArray_UINT8, 1, 1); + if (!codes) + throw Py::ValueError("Invalid codes array."); + + path = new PathAgg(); + + size_t next_vertex_stride = vertices->strides[0]; + size_t next_axis_stride = vertices->strides[1]; + size_t code_stride = codes->strides[0]; + + const char* vertex_i = vertices->data; + const char* code_i = codes->data; + const char* vertex_end = vertex_i + (vertices->dimensions[0] * vertices->strides[0]); + + size_t N = codes->dimensions[0]; + double x0, y0, x1, y1, x2, y2; + + for (size_t i = 0; i < N; ++i) { + switch (*(unsigned char*)(code_i)) { + case MOVETO: + GET_NEXT_VERTEX(x0, y0); + path->move_to(x0, y0); + _VERBOSE("MOVETO"); + break; + case LINETO: + GET_NEXT_VERTEX(x0, y0); + path->line_to(x0, y0); + _VERBOSE("LINETO"); + break; + case CURVE3: + GET_NEXT_VERTEX(x0, y0); + GET_NEXT_VERTEX(x1, y1); + path->curve3(x0, y0, x1, y1); + path->curvy = true; + _VERBOSE("CURVE3"); + break; + case CURVE4: + GET_NEXT_VERTEX(x0, y0); + GET_NEXT_VERTEX(x1, y1); + GET_NEXT_VERTEX(x2, y2); + path->curve4(x0, y0, x1, y1, x2, y2); + path->curvy = true; + _VERBOSE("CURVE4"); + break; + case CLOSEPOLY: + path->close_polygon(); + _VERBOSE("CLOSEPOLY"); + break; + } + code_i += code_stride; + } + } catch(...) { + Py_XDECREF(vertices); + Py_XDECREF(codes); + delete path; + throw; + } + + Py_XDECREF(vertices); + Py_XDECREF(codes); + + return Py::asObject(path); +} + +Py::Object +RendererAgg::draw_path(const Py::Tuple& args) { + typedef agg::conv_transform<agg::path_storage> transformed_path_t; + typedef agg::conv_curve<transformed_path_t> curve_t; + typedef agg::conv_stroke<curve_t> stroke_t; + typedef agg::conv_dash<curve_t> dash_t; + typedef agg::conv_stroke<dash_t> stroke_dash_t; + //draw_path(gc, rgbFace, path, transform) + theRasterizer->reset_clipping(); + + _VERBOSE("RendererAgg::draw_path"); + args.verify_length(4); + + GCAgg gc = GCAgg(args[0], dpi); + Py::Object path_obj = args[1]; + if (!PathAgg::check(path_obj)) + throw Py::TypeError("Native path object is not of correct type"); + PathAgg* path = static_cast<PathAgg*>(path_obj.ptr()); + agg::trans_affine trans = py_sequence_to_agg_transformation_matrix(args[2]); + facepair_t face = _get_rgba_face(args[3], gc.alpha); + + trans *= agg::trans_affine_scaling(1.0, -1.0); + trans *= agg::trans_affine_translation(0.0, (double)height); + + transformed_path_t tpath(*path, trans); + // MGDTODO: See if there is any advantage to only curving if necessary + curve_t curve(tpath); + + set_clipbox_rasterizer(gc.cliprect); + + if (face.first) { + rendererAA->color(face.second); + theRasterizer->add_path(curve); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } + + if (gc.linewidth) { + if (gc.dasha == NULL) { + stroke_t stroke(curve); + stroke.width(gc.linewidth); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + theRasterizer->add_path(stroke); + } else { + dash_t dash(curve); + for (size_t i = 0; i < (gc.Ndash / 2); ++i) + dash.add_dash(gc.dasha[2 * i], gc.dasha[2 * i + 1]); + stroke_dash_t stroke(dash); + stroke.line_cap(gc.cap); + stroke.line_join(gc.join); + stroke.width(gc.linewidth); + theRasterizer->add_path(stroke); //boyle freeze is herre + } + + if ( gc.isaa ) { + rendererAA->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineP8, *rendererAA); + } + else { + rendererBin->color(gc.color); + agg::render_scanlines(*theRasterizer, *slineBin, *rendererBin); + } + } + + return Py::Object(); +} + + +Py::Object RendererAgg::write_rgba(const Py::Tuple& args) { _VERBOSE("RendererAgg::write_rgba"); @@ -1949,6 +2052,10 @@ "draw_ellipse(gc, rgbFace, x, y, w, h)\n"); add_varargs_method("draw_polygon", &RendererAgg::draw_polygon, "draw_polygon(gc, rgbFace, points)\n"); + add_varargs_method("draw_path", &RendererAgg::draw_path, + "draw_path(gc, rgbFace, native_path, transform)\n"); + add_varargs_method("convert_to_native_path", &RendererAgg::convert_to_native_path, + "convert_to_native_path(vertices, codes)\n"); add_varargs_method("draw_lines", &RendererAgg::draw_lines, "draw_lines(gc, x, y,)\n"); add_varargs_method("draw_markers", &RendererAgg::draw_markers, @@ -1976,10 +2083,13 @@ add_varargs_method("restore_region", &RendererAgg::restore_region, "restore_region(region)"); - - } +void PathAgg::init_type() +{ + behaviors().name("PathAgg"); + behaviors().doc("A native Agg path object"); +} extern "C" DL_EXPORT(void) Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-09-14 13:03:31 UTC (rev 3851) +++ branches/transforms/src/_backend_agg.h 2007-09-14 17:57:52 UTC (rev 3852) @@ -39,6 +39,14 @@ #include "agg_scanline_p.h" #include "agg_vcgen_markers_term.h" +// These are copied directly from path.py, and must be kept in sync +#define STOP 0 +#define MOVETO 1 +#define LINETO 2 +#define CURVE3 3 +#define CURVE4 4 +#define CLOSEPOLY 0x0F + typedef agg::pixfmt_rgba32 pixfmt; typedef agg::renderer_base<pixfmt> renderer_base; typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa; @@ -163,6 +171,8 @@ Py::Object draw_markers(const Py::Tuple & args); Py::Object draw_text_image(const Py::Tuple & args); Py::Object draw_image(const Py::Tuple & args); + Py::Object draw_path(const Py::Tuple & args); + Py::Object convert_to_native_path(const Py::Tuple & args); Py::Object write_rgba(const Py::Tuple & args); Py::Object write_png(const Py::Tuple & args); @@ -229,7 +239,22 @@ agg::path_storage *lastclippath; }; +// A completely opaque data type used only to pass native path +// data to/from Python. Python can't do anything with the data +// other than create and then use it. +class PathAgg : + public agg::path_storage, + public Py::PythonExtension<PathAgg> { +public: + PathAgg() : curvy(false) {} + + static void init_type(void); + + bool curvy; +}; + + // the extension module class _backend_agg_module : public Py::ExtensionModule<_backend_agg_module> { @@ -240,6 +265,7 @@ BufferRegion::init_type(); RendererAgg::init_type(); + PathAgg::init_type(); add_keyword_method("RendererAgg", &_backend_agg_module::new_renderer, "RendererAgg(width, height, dpi)"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-14 13:03:33
|
Revision: 3851 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3851&view=rev Author: mdboom Date: 2007-09-14 06:03:31 -0700 (Fri, 14 Sep 2007) Log Message: ----------- Removed transforms on the C++ side -- removed many methods that depend on it in backend_agg in preparation for path generalization. Lots of general renaming... Modified Paths: -------------- branches/transforms/lib/matplotlib/artist.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/finance.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/table.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/ticker.py branches/transforms/lib/matplotlib/widgets.py branches/transforms/setup.py branches/transforms/setupext.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h branches/transforms/src/_gtkagg.cpp branches/transforms/src/_tkagg.cpp Added Paths: ----------- branches/transforms/lib/matplotlib/transforms.py Removed Paths: ------------- branches/transforms/lib/matplotlib/affine.py branches/transforms/src/_transforms.cpp branches/transforms/src/_transforms.h Deleted: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -1,854 +0,0 @@ -""" -A set of classes to handle transformations. - -2007 Michael Droettboom -""" - -import numpy as npy -from numpy.linalg import inv -from sets import Set - -# MGDTODO: The name of this module is bad, since it deals with -# non-affine transformations as well. It should probably just be -# "transforms", but we already had one of those... ;) - -# MGDTODO: This creates a ton of cyclical references. We may want to -# consider using weak references - -# MGDTODO: deep copying is probably incorrect wrt the parent/child -# relationships - -class TransformNode(object): - def __init__(self): - self._parents = Set() - - def invalidate(self): - if not self._do_invalidation(): - for parent in self._parents: - parent.invalidate() - - def _do_invalidation(self): - return False - - def set_children(self, children): - for child in children: - getattr(self, child)._parents.add(self) - self._children = children - -# 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): - ''' - This is the read-only part of a bounding-box - ''' - - def __init__(self): - TransformNode.__init__(self) - - def __array__(self): - return self.get_points() - - # MGDTODO: Probably a more efficient ways to do this... - def _get_xmin(self): - return self.get_points()[0, 0] - xmin = property(_get_xmin) - - def _get_ymin(self): - return self.get_points()[0, 1] - ymin = property(_get_ymin) - - def _get_xmax(self): - return self.get_points()[1, 0] - xmax = property(_get_xmax) - - def _get_ymax(self): - return self.get_points()[1, 1] - ymax = property(_get_ymax) - - def _get_min(self): - return self.get_points()[0] - min = property(_get_min) - - def _get_max(self): - return self.get_points()[1] - max = property(_get_max) - - def _get_intervalx(self): - return self.get_points()[:, 0] - intervalx = property(_get_intervalx) - - def _get_intervaly(self): - return self.get_points()[:, 1] - intervaly = property(_get_intervaly) - - def _get_width(self): - return self.xmax - self.xmin - width = property(_get_width) - - def _get_height(self): - return self.ymax - self.ymin - height = property(_get_height) - - def _get_bounds(self): - return (self.xmin, self.ymin, - self.xmax - self.xmin, self.ymax - self.ymin) - bounds = property(_get_bounds) - - def get_points(self): - return NotImplementedError() - - # MGDTODO: Optimize - def containsx(self, x): - return x >= self.xmin and x <= self.xmax - - def containsy(self, y): - return y >= self.ymin and y <= self.ymax - - def contains(self, x, y): - return self.containsx(x) and self.containsy(y) - - def overlapsx(self, other): - return self.containsx(other.xmin) \ - or self.containsx(other.xmax) - - def overlapsy(self, other): - return self.containsy(other.ymin) \ - or self.containsx(other.ymax) - - def overlaps(self, other): - return self.overlapsx(other) \ - and self.overlapsy(other) - - def fully_containsx(self, x): - return x > self.xmin and x < self.xmax - - def fully_containsy(self, y): - return y > self.ymin and y < self.ymax - - def fully_contains(self, x, y): - return self.fully_containsx(x) \ - and self.fully_containsy(y) - - def fully_overlapsx(self, other): - return self.fully_containsx(other.xmin) \ - or self.fully_containsx(other.xmax) - - def fully_overlapsy(self, other): - return self.fully_containsy(other.ymin) \ - or self.fully_containsx(other.ymax) - - def fully_overlaps(self, other): - return self.fully_overlapsx(other) and \ - self.fully_overlapsy(other) - - -class Bbox(BboxBase): - def __init__(self, points): - BboxBase.__init__(self) - self._points = npy.asarray(points, npy.float_) - - #@staticmethod - def unit(): - return Bbox.from_lbrt(0., 0., 1., 1.) - unit = staticmethod(unit) - - #@staticmethod - def from_lbwh(left, bottom, width, height): - return Bbox.from_lbrt(left, bottom, left + width, bottom + height) - from_lbwh = staticmethod(from_lbwh) - - #@staticmethod - def from_lbrt(*args): - points = npy.array(args, dtype=npy.float_).reshape(2, 2) - return Bbox(points) - from_lbrt = staticmethod(from_lbrt) - - def __copy__(self): - return Bbox(self._points.copy()) - - def __deepcopy__(self, memo): - return Bbox(self._points.copy()) - - def __cmp__(self, other): - # MGDTODO: Totally suboptimal - if isinstance(other, Bbox) and (self._points == other._points).all(): - return 0 - return -1 - - def __repr__(self): - return 'Bbox(%s)' % repr(self._points) - __str__ = __repr__ - - # JDH: the update method will update the box limits from the - # existing limits and the new data; it appears here you are just - # using the new data. We use an "ignore" flag to specify whether - # you want to include the existing data or not in the update - def update_from_data(self, x, y, ignore=True): - self._points = npy.array( - [[x.min(), y.min()], [x.max(), y.max()]], - npy.float_) - self.invalidate() - - # MGDTODO: Probably a more efficient ways to do this... - def _set_xmin(self, val): - self._points[0, 0] = val - self.invalidate() - xmin = property(BboxBase._get_xmin, _set_xmin) - - def _set_ymin(self, val): - self._points[0, 1] = val - self.invalidate() - ymin = property(BboxBase._get_ymin, _set_ymin) - - def _set_xmax(self, val): - self._points[1, 0] = val - self.invalidate() - xmax = property(BboxBase._get_xmax, _set_xmax) - - def _set_ymax(self, val): - self._points[1, 1] = val - self.invalidate() - ymax = property(BboxBase._get_ymax, _set_ymax) - - def _set_min(self, val): - self._points[0] = val - self.invalidate() - min = property(BboxBase._get_min, _set_min) - - def _set_max(self, val): - self._points[1] = val - self.invalidate() - max = property(BboxBase._get_max, _set_max) - - def _set_intervalx(self, interval): - self._points[:, 0] = interval - self.invalidate() - intervalx = property(BboxBase._get_intervalx, _set_intervalx) - - def _set_intervaly(self, interval): - self._points[:, 1] = interval - self.invalidate() - intervaly = property(BboxBase._get_intervaly, _set_intervaly) - - def _set_bounds(self, bounds): - l,b,w,h = bounds - self._points = npy.array([[l, b], [l+w, b+h]], npy.float_) - self.invalidate() - bounds = property(BboxBase._get_bounds, _set_bounds) - - def get_points(self): - return self._points - - def set_points(self, points): - self._points = points - self.invalidate() - - def set(self, other): - self._points = other.get_points() - self.invalidate() - - def transformed(self, transform): - return Bbox(transform(self._points)) - - def inverse_transformed(self, transform): - return Bbox(transform.inverted()(self._points)) - - def expanded(self, sw, 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) - - #@staticmethod - def union(bboxes): - """ - Return the Bbox that bounds all bboxes - """ - assert(len(bboxes)) - - if len(bboxes) == 1: - return bboxes[0] - - bbox = bboxes[0] - xmin = bbox.xmin - ymin = bbox.ymin - xmax = bbox.xmax - ymax = bbox.ymax - - for bbox in bboxes[1:]: - xmin = min(xmin, bbox.xmin) - ymin = min(ymin, bbox.ymin) - xmax = max(xmax, bbox.xmax) - ymax = max(ymax, bbox.ymax) - - return Bbox.from_lbrt(xmin, ymin, xmax, ymax) - union = staticmethod(union) - -class TransformedBbox(BboxBase): - def __init__(self, bbox, transform): - assert isinstance(bbox, Bbox) - assert isinstance(transform, Transform) - - BboxBase.__init__(self) - self.bbox = bbox - self.transform = transform - self.set_children(['bbox', 'transform']) - 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: - 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) - - def __call__(self, points): - raise NotImplementedError() - - def __add__(self, other): - if isinstance(other, Transform): - return composite_transform_factory(self, other) - raise TypeError( - "Can not add Transform to object of type '%s'" % type(other)) - - def __radd__(self, other): - if isinstance(other, Transform): - return composite_transform_factory(other, self) - raise TypeError( - "Can not add Transform to object of type '%s'" % type(other)) - - def transform_point(self, point): - return self.__call__(npy.asarray([point]))[0] - - def has_inverse(self): - raise NotImplementedError() - - def inverted(self): - raise NotImplementedError() - - def is_separable(self): - return False - - def is_affine(self): - return False - -class Affine2DBase(Transform): - input_dims = 2 - output_dims = 2 - - def __init__(self): - Transform.__init__(self) - self._inverted = None - - def _do_invalidation(self): - result = self._inverted is None - self._inverted = None - return result - - #@staticmethod - def _concat(a, b): - return npy.dot(b, a) - _concat = staticmethod(_concat) - - #@staticmethod - def concat(a, b): - return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix())) - concat = staticmethod(concat) - - def to_values(self): - mtx = self.get_matrix() - return tuple(mtx[:2].swapaxes(0, 1).flatten()) - - #@staticmethod - def matrix_from_values(a, b, c, d, e, f): - affine = npy.zeros((3, 3), npy.float_) - affine[0, ] = a, c, e - affine[1, ] = b, d, f - affine[2, 2] = 1 - return affine - matrix_from_values = staticmethod(matrix_from_values) - - def get_matrix(self): - raise NotImplementedError() - - def __call__(self, points): - """ - Applies the transformation to an array of 2D points and - returns the result. - - points must be a numpy array of shape (N, 2), where N is the - number of points. - """ - # MGDTODO: The major speed trap here is just converting to - # the points to an array in the first place. If we can use - # more arrays upstream, that should help here. - if not isinstance(points, npy.ndarray): - import traceback - print '-' * 60 - print 'A non-numpy array was passed in for transformation. Please ' - print 'correct this.' - print "".join(traceback.format_stack()) - print points - mtx = self.get_matrix() - points = npy.asarray(points, npy.float_) - points = points.transpose() - points = npy.dot(mtx[0:2, 0:2], points) - points = points + mtx[0:2, 2:] - return points.transpose() - - def inverted(self): - if self._inverted is None: - mtx = self.get_matrix() - self._inverted = Affine2D(inv(mtx)) - return self._inverted - - def is_separable(self): - mtx = self.get_matrix() - return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 - - def is_affine(self): - return True - - -class Affine2D(Affine2DBase): - input_dims = 2 - output_dims = 2 - - def __init__(self, matrix = None): - """ - Initialize an Affine transform from a 3x3 numpy float array. - - a c e - b d f - 0 0 1 - """ - Affine2DBase.__init__(self) - if matrix is None: - matrix = npy.identity(3) - else: - assert matrix.shape == (3, 3) - self._mtx = matrix - self._inverted = None - - def __repr__(self): - return "Affine2D(%s)" % repr(self._mtx) - __str__ = __repr__ - - def __cmp__(self, other): - if (isinstance(other, Affine2D) and - (self.get_matrix() == other.get_matrix()).all()): - return 0 - return -1 - - def __copy__(self): - return Affine2D(self._mtx.copy()) - - def __deepcopy__(self, memo): - return Affine2D(self._mtx.copy()) - - #@staticmethod - def from_values(a, b, c, d, e, f): - return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f)) - from_values = staticmethod(from_values) - - def get_matrix(self): - return self._mtx - - def set_matrix(self, mtx): - self._mtx = mtx - self.invalidate() - - def set(self, other): - self._mtx = other.get_matrix() - self.invalidate() - - #@staticmethod - def identity(): - return Affine2D(npy.identity(3)) - identity = staticmethod(identity) - - def clear(self): - self._mtx = npy.identity(3) - self.invalidate() - return self - - def rotate(self, theta): - a = npy.cos(theta) - b = npy.sin(theta) - rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0) - self._mtx = self._concat(self._mtx, rotate_mtx) - self.invalidate() - return self - - def rotate_deg(self, degrees): - return self.rotate(degrees*npy.pi/180.) - - def rotate_around(self, x, y, theta): - return self.translate(-x, -y).rotate(theta).translate(x, y) - - def rotate_deg_around(self, x, y, degrees): - return self.translate(-x, -y).rotate_deg(degrees).translate(x, y) - - def translate(self, tx, ty): - translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty) - self._mtx = self._concat(self._mtx, translate_mtx) - self.invalidate() - return self - - def scale(self, sx, sy=None): - if sy is None: - sy = sx - scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.) - self._mtx = self._concat(self._mtx, scale_mtx) - self.invalidate() - return self - - def inverted(self): - if self._inverted is None: - mtx = self.get_matrix() - self._inverted = Affine2D(inv(mtx)) - return self._inverted - - def is_separable(self): - mtx = self.get_matrix() - return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 - - def is_affine(self): - return True - -class BlendedTransform(Transform): - def __init__(self, x_transform, y_transform): - assert x_transform.is_separable() - assert y_transform.is_separable() - - Transform.__init__(self) - self._x = x_transform - self._y = y_transform - self.set_children(['_x', '_y']) - - def __call__(self, points): - if self._x == self._y: - return self._x(points) - - x_points = self._x(points) - y_points = self._y(points) - # This works because we already know the transforms are - # separable - return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) - -# def set_x_transform(self, x_transform): -# self.replace_child(0, x_transform) - -# def set_y_transform(self, y_transform): -# self.replace_child(1, y_transform) - -class BlendedAffine2D(Affine2DBase, BlendedTransform): - def __init__(self, x_transform, y_transform): - assert x_transform.is_affine() - assert y_transform.is_affine() - assert x_transform.is_separable() - assert y_transform.is_separable() - BlendedTransform.__init__(self, x_transform, y_transform) - - Affine2DBase.__init__(self) - self._mtx = None - - def __repr__(self): - return "BlendedAffine2D(%s,%s)" % (self._x, self._y) - __str__ = __repr__ - - def _do_invalidation(self): - if self._mtx is not None: - self._mtx = None - Affine2DBase._do_invalidation(self) - return False - return True - - def is_separable(self): - return True - - def get_matrix(self): - if self._mtx is None: - if self._x == self._y: - self._mtx = self._x.get_matrix() - else: - x_mtx = self._x.get_matrix() - y_mtx = self._y.get_matrix() - # This works because we already know the transforms are - # separable, though normally one would want to set b and - # c to zero. - self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) - return self._mtx - -class CompositeTransform(Transform): - def __init__(self, a, b): - assert a.output_dims == b.input_dims - self.input_dims = a.input_dims - self.output_dims = b.output_dims - - Transform.__init__(self) - self._a = a - self._b = b - self.set_children(['_a', '_b']) - - def __call__(self, points): - return self._b(self._a(points)) - -class CompositeAffine2D(Affine2DBase): - def __init__(self, a, b): - assert a.is_affine() - assert b.is_affine() - - Affine2DBase.__init__(self) - self._a = a - self._b = b - self.set_children(['_a', '_b']) - self._mtx = None - - def __repr__(self): - return "CompositeAffine2D(%s, %s)" % (self._a, self._b) - __str__ = __repr__ - - def _do_invalidation(self): - self._mtx = None - Affine2DBase._do_invalidation(self) - - def get_matrix(self): - if self._mtx is None: - self._mtx = self._concat( - self._a.get_matrix(), - self._b.get_matrix()) - return self._mtx - -class BboxTransform(Affine2DBase): - def __init__(self, boxin, boxout): - assert isinstance(boxin, BboxBase) - assert isinstance(boxout, BboxBase) - - Affine2DBase.__init__(self) - self._boxin = boxin - self._boxout = boxout - self.set_children(['_boxin', '_boxout']) - self._mtx = None - self._inverted = None - - def __repr__(self): - return "BboxTransform(%s, %s)" % (self._boxin, self._boxout) - __str__ = __repr__ - - def _do_invalidation(self): - if self._mtx is not None: - self._mtx = None - Affine2DBase._do_invalidation(self) - return False - return True - - def is_separable(self): - return True - - def get_matrix(self): - if self._mtx is None: - boxin = self._boxin - boxout = self._boxout - x_scale = boxout.width / boxin.width - y_scale = boxout.height / boxin.height - - # MGDTODO: Optimize - affine = Affine2D() \ - .translate(-boxin.xmin, -boxin.ymin) \ - .scale(x_scale, y_scale) \ - .translate(boxout.xmin, boxout.ymin) - - self._mtx = affine._mtx - return self._mtx - -def blend_xy_sep_transform(x_transform, y_transform): - if x_transform.is_affine() and y_transform.is_affine(): - return BlendedAffine2D(x_transform, y_transform) - return BlendedTransform(x_transform, y_transform) - -def composite_transform_factory(a, b): - if a.is_affine() and b.is_affine(): - return CompositeAffine2D(a, b) - return CompositeTransform(a, b) - -# 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. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped, - regardless of whether they are too close. - ''' - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -# MGDTODO: Optimize -def interval_contains(interval, val): - return interval[0] <= val and interval[1] >= val - -def interval_contains_open(interval, val): - return interval[0] < val and interval[1] > val - -if __name__ == '__main__': - import copy - from random import random - import timeit - - bbox = Bbox.from_lbrt(10., 15., 20., 25.) - assert bbox.xmin == 10 - assert bbox.ymin == 15 - assert bbox.xmax == 20 - assert bbox.ymax == 25 - - assert npy.all(bbox.min == [10, 15]) - assert npy.all(bbox.max == [20, 25]) - assert npy.all(bbox.intervalx == (10, 20)) - assert npy.all(bbox.intervaly == (15, 25)) - - assert bbox.width == 10 - assert bbox.height == 10 - - assert bbox.bounds == (10, 15, 10, 10) - - print npy.asarray(bbox) - - bbox.intervalx = (11, 21) - bbox.intervaly = (16, 26) - - assert bbox.bounds == (11, 16, 10, 10) - - bbox.xmin = 12 - bbox.ymin = 17 - bbox.xmax = 22 - bbox.ymax = 27 - - assert bbox.bounds == (12, 17, 10, 10) - - bbox = Bbox.from_lbwh(10, 11, 12, 13) - assert bbox.bounds == (10, 11, 12, 13) - - bbox_copy = copy.copy(bbox) - assert bbox == bbox_copy - bbox_copy.max = (14, 15) - assert bbox.bounds == (10, 11, 12, 13) - assert bbox_copy.bounds == (10, 11, 4, 4) - - bbox1 = Bbox([[10., 15.], [20., 25.]]) - bbox2 = Bbox([[30., 35.], [40., 45.]]) - trans = BboxTransform(bbox1, bbox2) - bbox3 = bbox1.transformed(trans) - assert bbox3 == bbox2 - - translation = Affine2D().translate(10, 20) - assert translation.to_values() == (1, 0, 0, 1, 10, 20) - scale = Affine2D().scale(10, 20) - assert scale.to_values() == (10, 0, 0, 20, 0, 0) - rotation = Affine2D().rotate_deg(30) - print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, - -0.49999999999999994, 0.86602540378443871, - 0.0, 0.0) - - points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_) - translated_points = translation(points) - assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all() - scaled_points = scale(points) - print scaled_points - rotated_points = rotation(points) - print rotated_points - - tpoints1 = rotation(translation(scale(points))) - trans_sum = scale + translation + rotation - tpoints2 = trans_sum(points) - print tpoints1, tpoints2 - print tpoints1 == tpoints2 - # Need to do some sort of fuzzy comparison here? - # assert (tpoints1 == tpoints2).all() - - # Here are some timing tests - points = npy.asarray([(random(), random()) for i in xrange(10000)]) - t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points") - print "Time to transform 10000 x 10 points:", t.timeit(10) - -__all__ = ['Transform', 'Affine2D'] Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/artist.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -1,7 +1,7 @@ from __future__ import division import sys, re from cbook import iterable, flatten -from affine import Affine2D +from transforms import Affine2D import matplotlib.units as units ## Note, matplotlib artists use the doc strings for set and get Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -9,7 +9,6 @@ rcParams = matplotlib.rcParams from matplotlib import artist as martist -from matplotlib import affine as maffine from matplotlib import agg from matplotlib import axis as maxis from matplotlib import cbook @@ -29,6 +28,7 @@ from matplotlib import table as mtable from matplotlib import text as mtext from matplotlib import ticker as mticker +from matplotlib import transforms as mtransforms iterable = cbook.iterable is_string_like = cbook.is_string_like @@ -481,7 +481,7 @@ """ martist.Artist.__init__(self) - self._position = maffine.Bbox.from_lbwh(*rect) + self._position = mtransforms.Bbox.from_lbwh(*rect) self._originalPosition = copy.deepcopy(self._position) self.set_axes(self) self.set_aspect('auto') @@ -612,7 +612,7 @@ """ martist.Artist.set_figure(self, fig) - self.bbox = maffine.TransformedBbox(self._position, fig.transFigure) + self.bbox = mtransforms.TransformedBbox(self._position, fig.transFigure) #these will be updated later as data is added self._set_lim_and_transforms() @@ -631,7 +631,7 @@ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ - Bbox = maffine.Bbox + Bbox = mtransforms.Bbox self.viewLim = Bbox.unit() if self._sharex is not None: @@ -647,20 +647,11 @@ self.dataLim = Bbox.unit() - self.transAxes = maffine.BboxTransform( + self.transAxes = mtransforms.BboxTransform( Bbox.unit(), self.bbox) - localTransData = maffine.BboxTransform( + self.transData = mtransforms.BboxTransform( self.viewLim, self.bbox) - if self._sharex: - transDataX = self._sharex.transData - else: - transDataX = localTransData - if self._sharey: - transDataY = self._sharey.transData - else: - transDataY = localTransData - self.transData = localTransData # maffine.blend_xy_sep_transform(transDataX, transDataY) def get_position(self, original=False): @@ -1538,7 +1529,7 @@ # and min(xmin, xmax)<=0): # raise ValueError('Cannot set nonpositive limits with log transform') - xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False) + xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False) self.viewLim.intervalx = (xmin, xmax) if emit: @@ -1670,7 +1661,7 @@ # and min(ymin, ymax)<=0): # raise ValueError('Cannot set nonpositive limits with log transform') - ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False) + ymin, ymax = mtransforms.nonsingular(ymin, ymax, increasing=False) self.viewLim.intervaly = (ymin, ymax) if emit: self.callbacks.process('ylim_changed', self) @@ -2167,7 +2158,7 @@ %(Annotation)s """ a = mtext.Annotation(*args, **kwargs) - a.set_transform(maffine.Affine2D()) + a.set_transform(mtransforms.Affine2D()) self._set_artist_props(a) if kwargs.has_key('clip_on'): a.set_clip_box(self.bbox) self.texts.append(a) @@ -2206,7 +2197,7 @@ %(Line2D)s """ - trans = maffine.blend_xy_sep_transform(self.transAxes, self.transData) + trans = mtransforms.blend_xy_sep_transform(self.transAxes, self.transData) l, = self.plot([xmin,xmax], [y,y], transform=trans, scalex=False, **kwargs) return l @@ -2242,7 +2233,7 @@ %(Line2D)s """ - trans = maffine.blend_xy_sep_transform( self.transData, self.transAxes ) + trans = mtransforms.blend_xy_sep_transform( self.transData, self.transAxes ) l, = self.plot([x,x], [ymin,ymax] , transform=trans, scaley=False, **kwargs) return l @@ -2281,7 +2272,7 @@ %(Polygon)s """ # convert y axis units - trans = maffine.blend_xy_sep_transform( self.transAxes, self.transData) + trans = mtransforms.blend_xy_sep_transform( self.transAxes, self.transData) verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) @@ -2321,7 +2312,7 @@ %(Polygon)s """ # convert x axis units - trans = maffine.blend_xy_sep_transform(self.transData, self.transAxes) + trans = mtransforms.blend_xy_sep_transform(self.transData, self.transAxes) verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) p.set_transform(trans) @@ -4104,7 +4095,7 @@ offsets = zip(x,y), transOffset = self.transData, ) - collection.set_transform(maffine.Affine2D()) + collection.set_transform(mtransforms.Affine2D()) collection.set_alpha(alpha) collection.update(kwargs) Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/axis.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -15,10 +15,10 @@ from ticker import NullFormatter, FixedFormatter, ScalarFormatter, LogFormatter from ticker import NullLocator, FixedLocator, LinearLocator, LogLocator, AutoLocator -from affine import Affine2D, Bbox, blend_xy_sep_transform, interval_contains, \ - interval_contains_open from font_manager import FontProperties from text import Text, TextWithDash, _process_text_args +from transforms import Affine2D, Bbox, blended_transform_factory, interval_contains, \ + interval_contains_open from patches import bbox_artist import matplotlib.units as units @@ -236,7 +236,7 @@ xaxis=True, ) - trans = blend_xy_sep_transform( + trans = blended_transform_factory( self.axes.transData, self.axes.transAxes) #offset the text downward with a post transformation trans = trans + Affine2D().translate(0, -1 * self._padPixels) @@ -261,7 +261,7 @@ horizontalalignment='center', ) - trans = blend_xy_sep_transform( + trans = blended_transform_factory( self.axes.transData, self.axes.transAxes) # offset the text upward with a post transformation trans = trans + Affine2D().translate(0, self._padPixels) @@ -279,7 +279,7 @@ marker = self._xtickmarkers[0], markersize=self._size, ) - l.set_transform(blend_xy_sep_transform( + l.set_transform(blended_transform_factory( self.axes.transData, self.axes.transAxes) ) self._set_artist_props(l) return l @@ -295,7 +295,7 @@ markersize=self._size, ) - l.set_transform(blend_xy_sep_transform( + l.set_transform(blended_transform_factory( self.axes.transData, self.axes.transAxes) ) self._set_artist_props(l) return l @@ -310,7 +310,7 @@ antialiased=False, ) l.set_transform( - blend_xy_sep_transform( + blended_transform_factory( self.axes.transData, self.axes.transAxes)) l.set_clip_box(self.axes.bbox) self._set_artist_props(l) @@ -359,7 +359,7 @@ dashdirection=0, xaxis=False, ) - trans = blend_xy_sep_transform( + trans = blended_transform_factory( self.axes.transAxes, self.axes.transData) # offset the text leftward with a post transformation trans = trans + Affine2D().translate(-1 * self._padPixels, 0) @@ -382,7 +382,7 @@ xaxis=False, horizontalalignment='left', ) - trans = blend_xy_sep_transform( + trans = blended_transform_factory( self.axes.transAxes, self.axes.transData) # offset the text rightward with a post transformation trans = trans + Affine2D().translate(self._padPixels, 0) @@ -401,7 +401,7 @@ markersize=self._size, ) l.set_transform( - blend_xy_sep_transform( + blended_transform_factory( self.axes.transAxes, self.axes.transData)) self._set_artist_props(l) return l @@ -417,7 +417,7 @@ ) l.set_transform( - blend_xy_sep_transform( + blended_transform_factory( self.axes.transAxes, self.axes.transData)) self._set_artist_props(l) return l @@ -432,7 +432,7 @@ antialiased=False, ) - l.set_transform( blend_xy_sep_transform( + l.set_transform( blended_transform_factory( self.axes.transAxes, self.axes.transData) ) l.set_clip_box(self.axes.bbox) @@ -979,7 +979,7 @@ verticalalignment='top', horizontalalignment='center', ) - label.set_transform( blend_xy_sep_transform( + label.set_transform( blended_transform_factory( self.axes.transAxes, Affine2D() )) self._set_artist_props(label) @@ -994,7 +994,7 @@ verticalalignment='top', horizontalalignment='right', ) - offsetText.set_transform( blend_xy_sep_transform( + offsetText.set_transform( blended_transform_factory( self.axes.transAxes, Affine2D() )) self._set_artist_props(offsetText) self.offset_text_position='bottom' @@ -1169,7 +1169,7 @@ horizontalalignment='right', rotation='vertical', ) - label.set_transform( blend_xy_sep_transform( + label.set_transform( blended_transform_factory( Affine2D(), self.axes.transAxes) ) self._set_artist_props(label) @@ -1184,7 +1184,7 @@ verticalalignment = 'bottom', horizontalalignment = 'left', ) - offsetText.set_transform(blend_xy_sep_transform( + offsetText.set_transform(blended_transform_factory( self.axes.transAxes, Affine2D()) ) self._set_artist_props(offsetText) self.offset_text_position='left' Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -84,7 +84,7 @@ from matplotlib.font_manager import findfont from matplotlib.ft2font import FT2Font, LOAD_DEFAULT from matplotlib.mathtext import MathTextParser -from matplotlib.affine import Bbox +from matplotlib.transforms import Bbox from _backend_agg import RendererAgg as _RendererAgg @@ -115,15 +115,10 @@ 'debug-annoying') # self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle - self.draw_path = self._renderer.draw_path # MGDTODO -- remove these lines # self.draw_lines = self._renderer.draw_lines # self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image - self.draw_line_collection = self._renderer.draw_line_collection - self.draw_quad_mesh = self._renderer.draw_quad_mesh - self.draw_poly_collection = self._renderer.draw_poly_collection - self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/figure.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -18,8 +18,8 @@ from text import Text, _process_text_args from legend import Legend -from affine import Affine2D, Bbox, BboxTransform, TransformedBbox from ticker import FormatStrFormatter +from transforms import Affine2D, Bbox, BboxTransform, TransformedBbox from cm import ScalarMappable from contour import ContourSet import warnings Modified: branches/transforms/lib/matplotlib/finance.py =================================================================== --- branches/transforms/lib/matplotlib/finance.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/finance.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -22,7 +22,7 @@ from matplotlib.colors import colorConverter from lines import Line2D, TICKLEFT, TICKRIGHT from patches import Rectangle -from matplotlib.affine import Affine2D +from matplotlib.transforms import Affine2D Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/legend.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -34,7 +34,7 @@ from patches import Patch, Rectangle, RegularPolygon, Shadow, bbox_artist, draw_bbox from collections import LineCollection, RegularPolyCollection, PatchCollection from text import Text -from affine import Bbox, BboxTransform +from transforms import Bbox, BboxTransform def line_cuts_bbox(line, bbox): """ Return True if and only if line cuts bbox. """ Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -14,10 +14,10 @@ import numerix.ma as ma from matplotlib import verbose import artist -from affine import Bbox from artist import Artist, setp from cbook import iterable, is_string_like, is_numlike from colors import colorConverter +from transforms import Bbox from matplotlib import rcParams Modified: branches/transforms/lib/matplotlib/table.py =================================================================== --- branches/transforms/lib/matplotlib/table.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/table.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -29,7 +29,7 @@ from patches import Rectangle from cbook import enumerate, is_string_like, flatten from text import Text -from affine import Bbox +from transforms import Bbox Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/text.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -15,7 +15,7 @@ from cbook import enumerate, is_string_like, maxdict, is_numlike from font_manager import FontProperties from patches import bbox_artist, YAArrow -from affine import Affine2D, Bbox +from transforms import Affine2D, Bbox from lines import Line2D import matplotlib.nxutils as nxutils Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-09-14 12:24:20 UTC (rev 3850) +++ branches/transforms/lib/matplotlib/ticker.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -109,7 +109,7 @@ import matplotlib as mpl from matplotlib import verbose, rcParams from matplotlib import cbook -from matplotlib import affine as maffine +from matplotlib import transforms as mtransforms @@ -540,7 +540,7 @@ def autoscale(self): 'autoscale the view limits' self.verify_intervals() - return maffine.nonsingular(*self.dataInterval.get_bounds()) + return mtransforms.nonsingular(*self.dataInterval.get_bounds()) def pan(self, numsteps): 'Pan numticks (can be positive or negative)' @@ -682,7 +682,7 @@ vmin = math.floor(scale*vmin)/scale vmax = math.ceil(scale*vmax)/scale - return maffine.nonsingular(vmin, vmax) + return mtransforms.nonsingular(vmin, vmax) def closeto(x,y): @@ -766,7 +766,7 @@ vmin -=1 vmax +=1 - return maffine.nonsingular(vmin, vmax) + return mtransforms.nonsingular(vmin, vmax) def scale_range(vmin, vmax, n = 1, threshold=100): dv = abs(vmax - vmin) @@ -833,12 +833,12 @@ def __call__(self): vmin, vmax = self.axis.get_view_interval() - vmin, vmax = maffine.nonsingular(vmin, vmax, expander = 0.05) + vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander = 0.05) return self.bin_boundaries(vmin, vmax) def autoscale(self): dmin, dmax = self.axis.get_data_interval() - dmin, dmax = maffine.nonsingular(dmin, dmax, expander = 0.05) + dmin, dmax = mtransforms.nonsingular(dmin, dmax, expander = 0.05) return npy.take(self.bin_boundaries(dmin, dmax), [0,-1]) @@ -939,7 +939,7 @@ if vmin==vmax: vmin = decade_down(vmin,self._base) vmax = decade_up(vmax,self._base) - return maffine.nonsingular(vmin, vmax) + return mtransforms.nonsingular(vmin, vmax) class AutoLocator(MaxNLocator): def __init__(self): Copied: branches/transforms/lib/matplotlib/transforms.py (from rev 3849, branches/transforms/lib/matplotlib/affine.py) =================================================================== --- branches/transforms/lib/matplotlib/transforms.py (rev 0) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 13:03:31 UTC (rev 3851) @@ -0,0 +1,852 @@ +""" +A set of classes to handle transformations. + +2007 Michael Droettboom +""" + +import numpy as npy +from numpy.linalg import inv +from sets import Set + +# MGDTODO: This creates a ton of cyclical references. We may want to +# consider using weak references + +# MGDTODO: deep copying is probably incorrect wrt the parent/child +# relationships + +class TransformNode(object): + def __init__(self): + self._parents = Set() + + def invalidate(self): + if not self._do_invalidation(): + for parent in self._parents: + parent.invalidate() + + def _do_invalidation(self): + return False + + def set_children(self, children): + 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): + ''' + This is the read-only part of a bounding-box + ''' + + def __init__(self): + TransformNode.__init__(self) + + def __array__(self): + return self.get_points() + + # MGDTODO: Probably a more efficient ways to do this... + def _get_xmin(self): + return self.get_points()[0, 0] + xmin = property(_get_xmin) + + def _get_ymin(self): + return self.get_points()[0, 1] + ymin = property(_get_ymin) + + def _get_xmax(self): + return self.get_points()[1, 0] + xmax = property(_get_xmax) + + def _get_ymax(self): + return self.get_points()[1, 1] + ymax = property(_get_ymax) + + def _get_min(self): + return self.get_points()[0] + min = property(_get_min) + + def _get_max(self): + return self.get_points()[1] + max = property(_get_max) + + def _get_intervalx(self): + return self.get_points()[:, 0] + intervalx = property(_get_intervalx) + + def _get_intervaly(self): + return self.get_points()[:, 1] + intervaly = property(_get_intervaly) + + def _get_width(self): + return self.xmax - self.xmin + width = property(_get_width) + + def _get_height(self): + return self.ymax - self.ymin + height = property(_get_height) + + def _get_bounds(self): + return (self.xmin, self.ymin, + self.xmax - self.xmin, self.ymax - self.ymin) + bounds = property(_get_bounds) + + def get_points(self): + return NotImplementedError() + + # MGDTODO: Optimize + def containsx(self, x): + return x >= self.xmin and x <= self.xmax + + def containsy(self, y): + return y >= self.ymin and y <= self.ymax + + def contains(self, x, y): + return self.containsx(x) and self.containsy(y) + + def overlapsx(self, other): + return self.containsx(other.xmin) \ + or self.containsx(other.xmax) + + def overlapsy(self, other): + return self.containsy(other.ymin) \ + or self.containsx(other.ymax) + + def overlaps(self, other): + return self.overlapsx(other) \ + and self.overlapsy(other) + + def fully_containsx(self, x): + return x > self.xmin and x < self.xmax + + def fully_containsy(self, y): + return y > self.ymin and y < self.ymax + + def fully_contains(self, x, y): + return self.fully_containsx(x) \ + and self.fully_containsy(y) + + def fully_overlapsx(self, other): + return self.fully_containsx(other.xmin) \ + or self.fully_containsx(other.xmax) + + def fully_overlapsy(self, other): + return self.fully_containsy(other.ymin) \ + or self.fully_containsx(other.ymax) + + def fully_overlaps(self, other): + return self.fully_overlapsx(other) and \ + self.fully_overlapsy(other) + + +class Bbox(BboxBase): + def __init__(self, points): + BboxBase.__init__(self) + self._points = npy.asarray(points, npy.float_) + + #@staticmethod + def unit(): + return Bbox.from_lbrt(0., 0., 1., 1.) + unit = staticmethod(unit) + + #@staticmethod + def from_lbwh(left, bottom, width, height): + return Bbox.from_lbrt(left, bottom, left + width, bottom + height) + from_lbwh = staticmethod(from_lbwh) + + #@staticmethod + def from_lbrt(*args): + points = npy.array(args, dtype=npy.float_).reshape(2, 2) + return Bbox(points) + from_lbrt = staticmethod(from_lbrt) + + def __copy__(self): + return Bbox(self._points.copy()) + + def __deepcopy__(self, memo): + return Bbox(self._points.copy()) + + def __cmp__(self, other): + # MGDTODO: Totally suboptimal + if isinstance(other, Bbox) and (self._points == other._points).all(): + return 0 + return -1 + + def __repr__(self): + return 'Bbox(%s)' % repr(self._points) + __str__ = __repr__ + + # JDH: the update method will update the box limits from the + # existing limits and the new data; it appears here you are just + # using the new data. We use an "ignore" flag to specify whether + # you want to include the existing data or not in the update + def update_from_data(self, x, y, ignore=True): + self._points = npy.array( + [[x.min(), y.min()], [x.max(), y.max()]], + npy.float_) + self.invalidate() + + # MGDTODO: Probably a more efficient ways to do this... + def _set_xmin(self, val): + self._points[0, 0] = val + self.invalidate() + xmin = property(BboxBase._get_xmin, _set_xmin) + + def _set_ymin(self, val): + self._points[0, 1] = val + self.invalidate() + ymin = property(BboxBase._get_ymin, _set_ymin) + + def _set_xmax(self, val): + self._points[1, 0] = val + self.invalidate() + xmax = property(BboxBase._get_xmax, _set_xmax) + + def _set_ymax(self, val): + self._points[1, 1] = val + self.invalidate() + ymax = property(BboxBase._get_ymax, _set_ymax) + + def _set_min(self, val): + self._points[0] = val + self.invalidate() + min = property(BboxBase._get_min, _set_min) + + def _set_max(self, val): + self._points[1] = val + self.invalidate() + max = property(BboxBase._get_max, _set_max) + + def _set_intervalx(self, interval): + self._points[:, 0] = interval + self.invalidate() + intervalx = property(BboxBase._get_intervalx, _set_intervalx) + + def _set_intervaly(self, interval): + self._points[:, 1] = interval + self.invalidate() + intervaly = property(BboxBase._get_intervaly, _set_intervaly) + + def _set_bounds(self, bounds): + l,b,w,h = bounds + self._points = npy.array([[l, b], [l+w, b+h]], npy.float_) + self.invalidate() + bounds = property(BboxBase._get_bounds, _set_bounds) + + def get_points(self): + return self._points + + def set_points(self, points): + self._points = points + self.invalidate() + + def set(self, other): + self._points = other.get_points() + self.invalidate() + + def transformed(self, transform): + return Bbox(transform(self._points)) + + def inverse_transformed(self, transform): + return Bbox(transform.inverted()(self._points)) + + def expanded(self, sw, 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) + + #@staticmethod + def union(bboxes): + """ + Return the Bbox that bounds all bboxes + """ + assert(len(bboxes)) + + if len(bboxes) == 1: + return bboxes[0] + + bbox = bboxes[0] + xmin = bbox.xmin + ymin = bbox.ymin + xmax = bbox.xmax + ymax = bbox.ymax + + for bbox in bboxes[1:]: + xmin = min(xmin, bbox.xmin) + ymin = min(ymin, bbox.ymin) + xmax = max(xmax, bbox.xmax) + ymax = max(ymax, bbox.ymax) + + return Bbox.from_lbrt(xmin, ymin, xmax, ymax) + union = staticmethod(union) + +class TransformedBbox(BboxBase): + def __init__(self, bbox, transform): + assert isinstance(bbox, Bbox) + assert isinstance(transform, Transform) + + BboxBase.__init__(self) + self.bbox = bbox + self.transform = transform + self.set_children(['bbox', 'transform']) + 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: + 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) + + def __call__(self, points): + raise NotImplementedError() + + def __add__(self, other): + if isinstance(other, Transform): + return composite_transform_factory(self, other) + raise TypeError( + "Can not add Transform to object of type '%s'" % type(other)) + + def __radd__(self, other): + if isinstance(other, Transform): + return composite_transform_factory(other, self) + raise TypeError( + "Can not add Transform to object of type '%s'" % type(other)) + + def transform_point(self, point): + return self.__call__(npy.asarray([point]))[0] + + def has_inverse(self): + raise NotImplementedError() + + def inverted(self): + raise NotImplementedError() + + def is_separable(self): + return False + + def is_affine(self): + return False + +class Affine2DBase(Transform): + input_dims = 2 + output_dims = 2 + + def __init__(self): + Transform.__init__(self) + self._inverted = None + + def _do_invalidation(self): + result = self._inverted is None + self._inverted = None + return result + + #@staticmethod + def _concat(a, b): + return npy.dot(b, a) + _concat = staticmethod(_concat) + + #@staticmethod + def concat(a, b): + return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix())) + concat = staticmethod(concat) + + def to_values(self): + mtx = self.get_matrix() + return tuple(mtx[:2].swapaxes(0, 1).flatten()) + + #@staticmethod + def matrix_from_values(a, b, c, d, e, f): + affine = npy.zeros((3, 3), npy.float_) + affine[0, ] = a, c, e + affine[1, ] = b, d, f + affine[2, 2] = 1 + return affine + matrix_from_values = staticmethod(matrix_from_values) + + def get_matrix(self): + raise NotImplementedError() + + def __call__(self, points): + """ + Applies the transformation to an array of 2D points and + returns the result. + + points must be a numpy array of shape (N, 2), where N is the + number of points. + """ + # MGDTODO: The major speed trap here is just converting to + # the points to an array in the first place. If we can use + # more arrays upstream, that should help here. + if not isinstance(points, npy.ndarray): + import traceback + print '-' * 60 + print 'A non-numpy array was passed in for transformation. Please ' + print 'correct this.' + print "".join(traceback.format_stack()) + print points + mtx = self.get_matrix()... [truncated message content] |
From: <md...@us...> - 2007-09-14 12:24:22
|
Revision: 3850 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3850&view=rev Author: mdboom Date: 2007-09-14 05:24:20 -0700 (Fri, 14 Sep 2007) Log Message: ----------- Deleting this file to rename affine.py Removed Paths: ------------- branches/transforms/lib/matplotlib/transforms.py Deleted: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-14 12:23:06 UTC (rev 3849) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-14 12:24:20 UTC (rev 3850) @@ -1,652 +0,0 @@ -# """ -# The transforms module is broken into two parts, a collection of -# classes written in the extension module _transforms to handle -# efficient transformation of data, and some helper functions in -# transforms to make it easy to instantiate and use those objects. -# Hence the core of this module lives in _transforms. - -# The transforms class is built around the idea of a LazyValue. A -# LazyValue is a base class that defines a method get that returns the -# value. The concrete derived class Value wraps a float, and simply -# returns the value of that float. The concrete derived class BinOp -# allows binary operations on LazyValues, so you can add them, multiply -# them, etc. When you do something like - -# inches = Value(8) -# dpi = Value(72) -# width = inches * dpi - -# width is a BinOp instance (that tells you the width of the figure in -# pixels). Later, if the figure size in changed, ie we call - -# inches.set(10) - -# The width variable is automatically updated because it stores a -# pointer to the inches variable, not the value. Since a BinOp is also -# a lazy value, you can define binary operations on BinOps as well, such -# as - -# middle = Value(0.5) * width - -# Pairs of LazyValue instances can occur as instances of two classes: - -# pt = Point( Value(x), Value(y)) # where x, y are numbers -# pt.x(), pt.y() return Value(x), Value(y)) - -# iv = Interval( Value(x), Value(y)) -# iv.contains(z) returns True if z is in the closed interval -# iv.contains_open(z): same for open interval -# iv.span() returns y-x as a float -# iv.get_bounds() returns (x,y) as a tuple of floats -# iv.set_bounds(x, y) allows input of new floats -# iv.update(seq) updates the bounds to include all elements -# in a sequence of floats -# iv.shift(s) shifts the interval by s, a float - -# The bounding box class Bbox is also heavily used, and is defined by a -# lower left point ll and an upper right point ur. The points ll and ur -# are given by Point(x, y) instances, where x and y are LazyValues. So -# you can represent a point such as - -# ll = Point( Value(0), Value(0) ) # the origin -# ur = Point( width, height ) # the upper right of the figure - -# where width and height are defined as above, using the product of the -# figure width in inches and the dpi. This is, in face, how the Figure -# bbox is defined - -# bbox = Bbox(ll, ur) - -# A bbox basically defines an x,y coordinate system, with ll giving the -# lower left of the coordinate system and ur giving the upper right. - -# The bbox methods are - -# ll() - return the lower left Point -# ur() - return the upper right Point -# contains(x,y) - return True if self contains point -# overlaps(bbox) - return True if self overlaps bbox -# overlapsx(bbox) - return True if self overlaps bbox in the x interval -# overlapsy(bbox) - return True if self overlaps bbox in the y interval -# intervalx() - return the x Interval instance -# intervaly() - return the y interval instance -# get_bounds() - get the left, bottom, width, height bounding tuple -# update(xys, ignore) - update the bbox to bound all the xy tuples in -# xys; if ignore is true ignore the current contents of bbox and -# just bound the tuples. If ignore is false, bound self + tuples -# width() - return the width of the bbox -# height() - return the height of the bbox -# xmax() - return the x coord of upper right -# ymax() - return the y coord of upper right -# xmin() - return the x coord of lower left -# ymin() - return the y coord of lower left -# scale(sx,sy) - scale the bbox by sx, sy -# deepcopy() - return a deep copy of self (pointers are lost) - - -# The basic transformation maps one bbox to another, with an optional -# nonlinear transformation of one of coordinates (eg log scaling). - -# The base class for transformations is Transformation, and the concrete -# derived classes are SeparableTransformation and Affine. Earlier -# versions of matplotlib handled transformation of x and y separately -# (ie we assumed all transformations were separable) but this makes it -# difficult to do rotations or polar transformations, for example. All -# artists contain their own transformation, defaulting to the identity -# transform. - -# The signature of a separable transformation instance is - -# trans = SeparableTransformation(bbox1, bbox2, funcx, funcy) - -# where funcx and funcy operate on x and y. The typical linear -# coordinate transformation maps one bounding box to another, with funcx -# and funcy both identity. Eg, - -# transData = Transformation(viewLim, displayLim, -# Func(IDENTITY), Func(IDENTITY)) - -# maps the axes view limits to display limits. If the xaxis scaling is -# changed to log, one simply calls - -# transData.get_funcx().set_type(LOG10) - -# For more general transformations including rotation, the Affine class -# is provided, which is constructed with 6 LazyValue instances: -# a, b, c, d, tx, ty. These give the values of the matrix transformation - -# [xo = |a c| [xi + [tx -# yo] |b d| yi] ty] - -# where if sx, sy are the scaling components, tx, y are the translation -# components, and alpha is the rotation - -# a = sx*cos(alpha); -# b = -sx*sin(alpha); -# c = sy*sin(alpha); -# d = sy*cos(alpha); - -# The affine transformation can be accomplished for row vectors with a -# single matrix multiplication -# X_new = X_old * M -# where -# M = [ a b 0 -# c d 0 -# tx ty 1] -# and each X is the row vector [x, y, 1]. Hence M is -# the transpose of the matrix representation given in -# http://en.wikipedia.org/wiki/Affine_transformation, -# which is for the more usual column-vector representation -# of the position.) - - - -# From a user perspective, the most important Tranformation methods are - -# All transformations -# ------------------- -# freeze() - eval and freeze the lazy objects -# thaw() - release the lazy objects - -# xy_tup(xy) - transform the tuple (x,y) -# seq_x_y(x, y) - transform the python sequences x and y -# numerix_x_y(x, y) - x and y are numerix 1D arrays -# numerix_xy(xy) - xy is a numerix array of shape (N,2) -# inverse_numerix_xy(xy)- inverse of the above -# seq_xy_tups(seq) - seq is a sequence of xy tuples or a (N,2) array -# inverse_xy_tup(xy) - apply the inverse transformation to tuple xy - -# set_offset(xy, trans) - xy is an x,y tuple and trans is a -# Transformation instance. This will apply a post transformational -# offset of all future transformations by xt,yt = trans.xy_tup(xy[0], xy[1]) - -# deepcopy() - returns a deep copy; references are lost -# shallowcopy() - returns a shallow copy excluding the offset - -# Separable transformations -# ------------------------- - -# get_bbox1() - return the input bbox -# get_bbox2() - return the output bbox -# set_bbox1() - set the input bbox -# set_bbox2() - set the output bbox -# get_funcx() - return the Func instance on x -# get_funcy() - return the Func instance on y -# set_funcx() - set the Func instance on x -# set_funcy() - set the Func instance on y - - -# Affine transformations -# ---------------------- - -# as_vec6() - return the affine as length 6 list of Values - - -# In general, you shouldn't need to construct your own transformations, -# but should use the helper functions defined in this module. - - -# zero - return Value(0) -# one - return Value(1) -# origin - return Point(zero(), zero()) -# unit_bbox - return the 0,0 to 1,1 bounding box -# identity_affine - An affine identity transformation -# identity_transform - An identity separable transformation -# translation_transform - a pure translational affine -# scale_transform - a pure scale affine -# scale_sep_transform - a pure scale separable transformation -# scale_translation_transform - a scale and translate affine -# bound_vertices - return the bbox that bounds all the xy tuples -# bbox_all - return the bbox that bounds all the bboxes -# lbwh_to_bbox - build a bbox from tuple -# left, bottom, width, height tuple - -# multiply_affines - return the affine that is the matrix product of -# the two affines - -# get_bbox_transform - return a SeparableTransformation instance that -# transforms one bbox to another - -# blend_xy_sep_transform - mix the x and y components of two separable -# transformations into a new transformation. -# This allows you to specify x and y in -# different coordinate systems - -# transform_bbox - apply a transformation to a bbox and return the -# transformed bbox - -# inverse_transform_bbox - apply the inverse transformation of a bbox -# and return the inverse transformed bbox - -# offset_copy - make a copy with an offset - - -# The units/transform_unit.py code has many examples. - -# A related and partly overlapping class, PBox, has been added to the -# original transforms module to facilitate Axes repositioning and resizing. -# At present, the differences between Bbox and PBox include: - -# Bbox works with the bounding box, the coordinates of the lower-left -# and upper-right corners; PBox works with the lower-left coordinates -# and the width, height pair (left, bottom, width, height, or 'lbwh'). -# Obviously, these are equivalent, but lbwh is what is used by -# Axes._position, and it is the natural specification for the types of -# manipulations for which the PBox class was made. - -# Bbox uses LazyValues grouped in pairs as 'll' and 'ur' Point objects; -# PBox uses a 4-element list, subclassed from the python list. - -# Bbox and PBox methods are mostly quite different, reflecting their -# different original purposes. Similarly, the CXX implementation of -# Bbox is good for methods such as update and for lazy evaluation, but -# for PBox intended uses, involving very little calculation, pure -# python probably is adequate. - -# In the future we may reimplement the PBox using Bbox -# and transforms, or eliminate it entirely by adding its methods -# and attributes to Bbox and/or putting them elsewhere in this module. -# """ -# from __future__ import division -# import math -# import numpy as npy - -# from matplotlib._transforms import Value, Point, Interval, Bbox, Affine -# from matplotlib._transforms import IDENTITY, LOG10, POLAR, Func, FuncXY -# from matplotlib._transforms import SeparableTransformation -# from matplotlib._transforms import NonseparableTransformation - -# def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): -# ''' -# Ensure the endpoints of a range are not too close together. - -# "too close" means the interval is smaller than 'tiny' times -# the maximum absolute value. - -# If they are too close, each will be moved by the 'expander'. -# If 'increasing' is True and vmin > vmax, they will be swapped, -# regardless of whether they are too close. -# ''' -# swapped = False -# if vmax < vmin: -# vmin, vmax = vmax, vmin -# swapped = True -# if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: -# if vmin==0.0: -# vmin = -expander -# vmax = expander -# else: -# vmin -= expander*abs(vmin) -# vmax += expander*abs(vmax) -# if swapped and not increasing: -# vmin, vmax = vmax, vmin -# return vmin, vmax - - -# def zero(): return Value(0) - -# def one() : return Value(1) - -# def origin(): -# return Point( zero(), zero() ) - -# def unit_bbox(): -# """ -# Get a 0,0 -> 1,1 Bbox instance -# """ -# return Bbox( origin(), Point( one(), one() ) ) - -# def identity_affine(): -# """ -# Get an affine transformation that maps x,y -> x,y -# """ - -# return Affine(one(), zero(), zero(), one(), zero(), zero()) - -# def identity_transform(): -# """ -# Get an affine transformation that maps x,y -> x,y -# """ -# return SeparableTransformation(unit_bbox(), unit_bbox(), -# Func(IDENTITY), -# Func(IDENTITY)) - -# def translation_transform(tx, ty): -# """ -# return a pure tranlational transformation tx and ty are LazyValue -# instances (Values or binary operations on values) -# """ -# return Affine(one(), zero(), zero(), one(), tx, ty) - -# def scale_transform(sx, sy): -# """ -# Return a pure scale transformation as an Affine instance; sx and -# sy are LazyValue instances (Values or binary opertations on -# values) -# """ -# return Affine(sx, zero(), zero(), sy, zero(), zero()) - -# def scale_sep_transform(sx, sy): -# """ -# Return a pure scale transformation as a SeparableTransformation; -# sx and sy are LazyValue instances (Values or binary opertations on -# values) -# """ - -# bboxin = unit_bbox() -# bboxout = Bbox( Point( zero(), zero() ), -# Point( sx, sy ) ) -# return SeparableTransformation( -# bboxin, bboxout, -# Func(IDENTITY), Func(IDENTITY)) - - - -# def bound_vertices(verts): -# """ -# Return the Bbox of the sequence of x,y tuples in verts -# """ -# # this is a good candidate to move to _transforms -# bbox = unit_bbox() -# bbox.update(verts, 1) -# return bbox - -# def bbox_all(bboxes): -# """ -# Return the Bbox that bounds all bboxes -# """ -# # this is a good candidate to move to _transforms -# assert(len(bboxes)) - -# if len(bboxes)==1: return bboxes[0] - -# bbox = bboxes[0] -# minx = bbox.xmin() -# miny = bbox.ymin() -# maxx = bbox.xmax() -# maxy = bbox.ymax() - -# for bbox in bboxes[1:]: -# thisminx = bbox.xmin() -# thisminy = bbox.ymin() -# thismaxx = bbox.xmax() -# thismaxy = bbox.ymax() - -# if thisminx < minx : minx = thisminx -# if thismaxx > maxx : maxx = thismaxx -# if thisminy < miny : miny = thisminy -# if thismaxy > maxy : maxy = thismaxy - -# return Bbox( Point( Value(minx), Value(miny) ), -# Point( Value(maxx), Value(maxy) ) -# ) - -# def lbwh_to_bbox(l,b,w,h): -# return Bbox( Point( Value(l), Value(b)), -# Point( Value(l+w), Value(b + h) ) ) - - -# def invert_vec6(v): -# """ -# v is a,b,c,d,tx,ty vec6 repr of an affine transformation. -# Return the inverse of v as a vec6 -# """ -# a,b,c,d,tx,ty = v -# M = npy.array([ [a,b,0], [c,d,0], [tx,ty,1]], dtype=npy.float64) -# Mi = npy.linalg.inv(M) -# return Mi[:,:2].ravel() - -# def multiply_affines( v1, v2): -# """ -# v1 and v2 are Affine instances -# """ - -# a1, b1, c1, d1, tx1, ty1 = v1.as_vec6() -# a2, b2, c2, d2, tx2, ty2 = v2.as_vec6() - -# a = a1 * a2 + c1 * b2 -# b = b1 * a2 + d1 * b2 -# c = a1 * c2 + c1 * d2 -# d = b1 * c2 + d1 * d2 -# tx = a1 * tx2 + c1 * ty2 + tx1 -# ty = b1 * tx2 + d1 * ty2 + ty1 -# return Affine(a,b,c,d,tx,ty) - -# def get_bbox_transform(boxin, boxout): -# """ -# return the transform that maps transform one bounding box to -# another -# """ -# return SeparableTransformation( -# boxin, boxout, Func(IDENTITY), Func( IDENTITY)) - - -# def blend_xy_sep_transform(trans1, trans2): -# """ -# If trans1 and trans2 are SeparableTransformation instances, you can -# build a new SeparableTransformation from them by extracting the x and y -# bounding points and functions and recomposing a new SeparableTransformation - -# This function extracts all the relevant bits from trans1 and -# trans2 and returns the new Transformation instance. This is -# useful, for example, if you want to specify x in data coordinates -# and y in axes coordinates. -# """ - -# inboxx = trans1.get_bbox1() -# inboxy = trans2.get_bbox1() - -# outboxx = trans1.get_bbox2() -# outboxy = trans2.get_bbox2() - -# xminIn = inboxx.ll().x() -# xmaxIn = inboxx.ur().x() -# xminOut = outboxx.ll().x() -# xmaxOut = outboxx.ur().x() - -# yminIn = inboxy.ll().y() -# ymaxIn = inboxy.ur().y() -# yminOut = outboxy.ll().y() -# ymaxOut = outboxy.ur().y() - -# funcx = trans1.get_funcx() -# funcy = trans2.get_funcy() - -# boxin = Bbox( Point(xminIn, yminIn), Point(xmaxIn, ymaxIn) ) -# boxout = Bbox( Point(xminOut, yminOut), Point(xmaxOut, ymaxOut) ) - -# return SeparableTransformation(boxin, boxout, funcx, funcy) - - -# def transform_bbox(trans, bbox): -# 'transform the bbox to a new bbox' -# xmin, xmax = bbox.intervalx().get_bounds() -# ymin, ymax = bbox.intervaly().get_bounds() - -# xmin, ymin = trans.xy_tup((xmin, ymin)) -# xmax, ymax = trans.xy_tup((xmax, ymax)) - -# return Bbox(Point(Value(xmin), Value(ymin)), -# Point(Value(xmax), Value(ymax))) - -# def inverse_transform_bbox(trans, bbox): -# 'inverse transform the bbox' -# xmin, xmax = bbox.intervalx().get_bounds() -# ymin, ymax = bbox.intervaly().get_bounds() - -# xmin, ymin = trans.inverse_xy_tup((xmin, ymin)) -# xmax, ymax = trans.inverse_xy_tup((xmax, ymax)) -# return Bbox(Point(Value(xmin), Value(ymin)), -# Point(Value(xmax), Value(ymax))) - - -# def offset_copy(trans, fig=None, x=0, y=0, units='inches'): -# ''' -# Return a shallow copy of a transform with an added offset. -# args: -# trans is any transform -# kwargs: -# fig is the current figure; it can be None if units are 'dots' -# x, y give the offset -# units is 'inches', 'points' or 'dots' -# ''' -# newtrans = trans.shallowcopy() -# if units == 'dots': -# newtrans.set_offset((x,y), identity_transform()) -# return newtrans -# if fig is None: -# raise ValueError('For units of inches or points a fig kwarg is needed') -# if units == 'points': -# x /= 72.0 -# y /= 72.0 -# elif not units == 'inches': -# raise ValueError('units must be dots, points, or inches') -# tx = Value(x) * fig.dpi -# ty = Value(y) * fig.dpi -# newtrans.set_offset((0,0), translation_transform(tx, ty)) -# return newtrans - - -# def get_vec6_scales(v): -# 'v is an affine vec6 a,b,c,d,tx,ty; return sx, sy' -# a,b,c,d = v[:4] -# sx = math.sqrt(a**2 + b**2) -# sy = math.sqrt(c**2 + d**2) -# return sx, sy - -# def get_vec6_rotation(v): -# 'v is an affine vec6 a,b,c,d,tx,ty; return rotation in degrees' -# sx, sy = get_vec6_scales(v) -# c,d = v[2:4] -# angle = math.atan2(c,d)/math.pi*180 -# return angle - - - -# class PBox(list): -# ''' -# A left-bottom-width-height (lbwh) specification of a bounding box, -# such as is used to specify the position of an Axes object within -# a Figure. -# It is a 4-element list with methods for changing the size, shape, -# and position relative to its container. -# ''' -# coefs = {'C': (0.5, 0.5), -# 'SW': (0,0), -# 'S': (0.5, 0), -# 'SE': (1.0, 0), -# 'E': (1.0, 0.5), -# 'NE': (1.0, 1.0), -# 'N': (0.5, 1.0), -# 'NW': (0, 1.0), -# 'W': (0, 0.5)} -# def __init__(self, box, container=None, llur=False): -# if len(box) != 4: -# raise ValueError("Argument must be iterable of length 4") -# if llur: -# box = [box[0], box[1], box[2]-box[0], box[3]-box[1]] -# list.__init__(self, box) -# self.set_container(container) - -# def as_llur(self): -# return [self[0], self[1], self[0]+self[2], self[1]+self[3]] - -# def set_container(self, box=None): -# if box is None: -# box = self -# if len(box) != 4: -# raise ValueError("Argument must be iterable of length 4") -# self._container = list(box) - -# def get_container(self, box): -# return self._container - -# def anchor(self, c, container=None): -# ''' -# Shift to position c within its container. - -# c can be a sequence (cx, cy) where cx, cy range from 0 to 1, -# where 0 is left or bottom and 1 is right or top. - -# Alternatively, c can be a string: C for centered, -# S for bottom-center, SE for bottom-left, E for left, etc. - -# Optional arg container is the lbwh box within which the -# PBox is positioned; it defaults to the initial -# PBox. -# ''' -# if container is None: -# container = self._container -# l,b,w,h = container -# if isinstance(c, str): -# cx, cy = self.coefs[c] -# else: -# cx, cy = c -# W,H = self[2:] -# self[:2] = l + cx * (w-W), b + cy * (h-H) -# return self - -# def shrink(self, mx, my): -# ''' -# Shrink the box by mx in the x direction and my in the y direction. -# The lower left corner of the box remains unchanged. -# Normally mx and my will be <= 1, but this is not enforced. -# ''' -# assert mx >= 0 and my >= 0 -# self[2:] = mx * self[2], my * self[3] -# return self - -# def shrink_to_aspect(self, box_aspect, fig_aspect = 1): -# ''' -# Shrink the box so that it is as large as it can be while -# having the desired aspect ratio, box_aspect. -# If the box coordinates are relative--that is, fractions of -# a larger box such as a figure--then the physical aspect -# ratio of that figure is specified with fig_aspect, so -# that box_aspect can also be given as a ratio of the -# absolute dimensions, not the relative dimensions. -# ''' -# assert box_aspect > 0 and fig_aspect > 0 -# l,b,w,h = self._container -# H = w * box_aspect/fig_aspect -# if H <= h: -# W = w -# else: -# W = h * fig_aspect/box_aspect -# H = h -# self[2:] = W,H -# return self - -# def splitx(self, *args): -# ''' -# e.g., PB.splitx(f1, f2, ...) - -# Returns a list of new PBoxes formed by -# splitting the original one (PB) with vertical lines -# at fractional positions f1, f2, ... -# ''' -# boxes = [] -# xf = [0] + list(args) + [1] -# l,b,w,h = self[:] -# for xf0, xf1 in zip(xf[:-1], xf[1:]): -# boxes.append(PBox([l+xf0*w, b, (xf1-xf0)*w, h])) -# return boxes - -# def splity(self, *args): -# ''' -# e.g., PB.splity(f1, f2, ...) - -# Returns a list of new PBoxes formed by -# splitting the original one (PB) with horizontal lines -# at fractional positions f1, f2, ..., with y measured -# positive up. -# ''' -# boxes = [] -# yf = [0] + list(args) + [1] -# l,b,w,h = self[:] -# for yf0, yf1 in zip(yf[:-1], yf[1:]): -# boxes.append(PBox([l, b+yf0*h, w, (yf1-yf0)*h])) -# return boxes - - - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-14 12:23:11
|
Revision: 3849 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3849&view=rev Author: mdboom Date: 2007-09-14 05:23:06 -0700 (Fri, 14 Sep 2007) Log Message: ----------- Committing this file so I can rename it Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-13 18:00:10 UTC (rev 3848) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-14 12:23:06 UTC (rev 3849) @@ -847,12 +847,8 @@ # assert (tpoints1 == tpoints2).all() # Here are some timing tests - points = [(random(), random()) for i in xrange(10000)] + points = npy.asarray([(random(), random()) for i in xrange(10000)]) t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points") - print "Time to transform 10000 x 10 points as tuples:", t.timeit(10) - - points2 = npy.asarray(points) - t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2") - print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10) + print "Time to transform 10000 x 10 points:", t.timeit(10) __all__ = ['Transform', 'Affine2D'] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-13 18:00:12
|
Revision: 3848 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3848&view=rev Author: mdboom Date: 2007-09-13 11:00:10 -0700 (Thu, 13 Sep 2007) Log Message: ----------- New milestone -- resizing figure window works. shared_axis_demo.py works. (Uses callbacks to track changes between axes's). Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py 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/figure.py branches/transforms/lib/matplotlib/pyplot.py branches/transforms/lib/matplotlib/text.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -20,284 +20,461 @@ class TransformNode(object): def __init__(self): - self._parents = Set() - + self._parents = Set() + def invalidate(self): - if not self._do_invalidation(): - for parent in self._parents: - parent.invalidate() + if not self._do_invalidation(): + for parent in self._parents: + parent.invalidate() def _do_invalidation(self): - return False - - def add_children(self, children): - for child in children: - child._parents.add(self) + return False -class Bbox(TransformNode): + def set_children(self, children): + for child in children: + getattr(self, child)._parents.add(self) + self._children = children + +# 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): + ''' + This is the read-only part of a bounding-box + ''' + + def __init__(self): + TransformNode.__init__(self) + + def __array__(self): + return self.get_points() + + # MGDTODO: Probably a more efficient ways to do this... + def _get_xmin(self): + return self.get_points()[0, 0] + xmin = property(_get_xmin) + + def _get_ymin(self): + return self.get_points()[0, 1] + ymin = property(_get_ymin) + + def _get_xmax(self): + return self.get_points()[1, 0] + xmax = property(_get_xmax) + + def _get_ymax(self): + return self.get_points()[1, 1] + ymax = property(_get_ymax) + + def _get_min(self): + return self.get_points()[0] + min = property(_get_min) + + def _get_max(self): + return self.get_points()[1] + max = property(_get_max) + + def _get_intervalx(self): + return self.get_points()[:, 0] + intervalx = property(_get_intervalx) + + def _get_intervaly(self): + return self.get_points()[:, 1] + intervaly = property(_get_intervaly) + + def _get_width(self): + return self.xmax - self.xmin + width = property(_get_width) + + def _get_height(self): + return self.ymax - self.ymin + height = property(_get_height) + + def _get_bounds(self): + return (self.xmin, self.ymin, + self.xmax - self.xmin, self.ymax - self.ymin) + bounds = property(_get_bounds) + + def get_points(self): + return NotImplementedError() + + # MGDTODO: Optimize + def containsx(self, x): + return x >= self.xmin and x <= self.xmax + + def containsy(self, y): + return y >= self.ymin and y <= self.ymax + + def contains(self, x, y): + return self.containsx(x) and self.containsy(y) + + def overlapsx(self, other): + return self.containsx(other.xmin) \ + or self.containsx(other.xmax) + + def overlapsy(self, other): + return self.containsy(other.ymin) \ + or self.containsx(other.ymax) + + def overlaps(self, other): + return self.overlapsx(other) \ + and self.overlapsy(other) + + def fully_containsx(self, x): + return x > self.xmin and x < self.xmax + + def fully_containsy(self, y): + return y > self.ymin and y < self.ymax + + def fully_contains(self, x, y): + return self.fully_containsx(x) \ + and self.fully_containsy(y) + + def fully_overlapsx(self, other): + return self.fully_containsx(other.xmin) \ + or self.fully_containsx(other.xmax) + + def fully_overlapsy(self, other): + return self.fully_containsy(other.ymin) \ + or self.fully_containsx(other.ymax) + + def fully_overlaps(self, other): + return self.fully_overlapsx(other) and \ + self.fully_overlapsy(other) + + +class Bbox(BboxBase): def __init__(self, points): - TransformNode.__init__(self) - self._points = npy.asarray(points, npy.float_) - self.track = False + BboxBase.__init__(self) + self._points = npy.asarray(points, npy.float_) #@staticmethod def unit(): - return Bbox.from_lbrt(0., 0., 1., 1.) + return Bbox.from_lbrt(0., 0., 1., 1.) unit = staticmethod(unit) #@staticmethod def from_lbwh(left, bottom, width, height): - return Bbox.from_lbrt(left, bottom, left + width, bottom + height) + return Bbox.from_lbrt(left, bottom, left + width, bottom + height) from_lbwh = staticmethod(from_lbwh) #@staticmethod def from_lbrt(*args): - points = npy.array(args, dtype=npy.float_).reshape(2, 2) - return Bbox(points) + points = npy.array(args, dtype=npy.float_).reshape(2, 2) + return Bbox(points) from_lbrt = staticmethod(from_lbrt) def __copy__(self): - return Bbox(self._points.copy()) + return Bbox(self._points.copy()) def __deepcopy__(self, memo): - return Bbox(self._points.copy()) + return Bbox(self._points.copy()) def __cmp__(self, other): - # MGDTODO: Totally suboptimal - if isinstance(other, Bbox) and (self._points == other._points).all(): - return 0 - return -1 + # MGDTODO: Totally suboptimal + if isinstance(other, Bbox) and (self._points == other._points).all(): + return 0 + return -1 def __repr__(self): - return 'Bbox(%s)' % repr(self._points) + return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ - def __array__(self): - return self._points - # JDH: the update method will update the box limits from the # existing limits and the new data; it appears here you are just # using the new data. We use an "ignore" flag to specify whether # you want to include the existing data or not in the update def update_from_data(self, x, y, ignore=True): - self._points = npy.array([[x.min(), y.min()], [x.max(), y.max()]], npy.float_) - self.invalidate() + self._points = npy.array( + [[x.min(), y.min()], [x.max(), y.max()]], + npy.float_) + self.invalidate() # MGDTODO: Probably a more efficient ways to do this... - def _get_xmin(self): - return self._points[0, 0] def _set_xmin(self, val): - self._points[0, 0] = val - self.invalidate() - xmin = property(_get_xmin, _set_xmin) + self._points[0, 0] = val + self.invalidate() + xmin = property(BboxBase._get_xmin, _set_xmin) - def _get_ymin(self): - return self._points[0, 1] def _set_ymin(self, val): - self._points[0, 1] = val - self.invalidate() - ymin = property(_get_ymin, _set_ymin) + self._points[0, 1] = val + self.invalidate() + ymin = property(BboxBase._get_ymin, _set_ymin) - def _get_xmax(self): - return self._points[1, 0] def _set_xmax(self, val): - self._points[1, 0] = val - self.invalidate() - xmax = property(_get_xmax, _set_xmax) + self._points[1, 0] = val + self.invalidate() + xmax = property(BboxBase._get_xmax, _set_xmax) - def _get_ymax(self): - return self._points[1, 1] def _set_ymax(self, val): - self._points[1, 1] = val - self.invalidate() - ymax = property(_get_ymax, _set_ymax) + self._points[1, 1] = val + self.invalidate() + ymax = property(BboxBase._get_ymax, _set_ymax) - def _get_min(self): - return self._points[0] def _set_min(self, val): - self._points[0] = val - self.invalidate() - min = property(_get_min, _set_min) + self._points[0] = val + self.invalidate() + min = property(BboxBase._get_min, _set_min) - def _get_max(self): - return self._points[1] def _set_max(self, val): - self._points[1] = val - self.invalidate() - max = property(_get_max, _set_max) + self._points[1] = val + self.invalidate() + max = property(BboxBase._get_max, _set_max) - def _get_intervalx(self): - return self._points[:,0] def _set_intervalx(self, interval): - self._points[:,0] = interval - self.invalidate() - intervalx = property(_get_intervalx, _set_intervalx) + self._points[:, 0] = interval + self.invalidate() + intervalx = property(BboxBase._get_intervalx, _set_intervalx) - def _get_intervaly(self): - return self._points[:,1] def _set_intervaly(self, interval): - self._points[:,1] = interval - self.invalidate() - intervaly = property(_get_intervaly, _set_intervaly) + self._points[:, 1] = interval + self.invalidate() + intervaly = property(BboxBase._get_intervaly, _set_intervaly) - def _get_width(self): - return self.xmax - self.xmin - width = property(_get_width) + def _set_bounds(self, bounds): + l,b,w,h = bounds + self._points = npy.array([[l, b], [l+w, b+h]], npy.float_) + self.invalidate() + bounds = property(BboxBase._get_bounds, _set_bounds) - def _get_height(self): - return self.ymax - self.ymin - height = property(_get_height) + def get_points(self): + return self._points - def _get_bounds(self): - return (self.xmin, self.ymin, - self.xmax - self.xmin, self.ymax - self.ymin) - def _set_bounds(self, bounds): - l,b,w,h = bounds - self._points = npy.array([[l, b], [l+w, b+h]], npy.float_) - self.invalidate() - bounds = property(_get_bounds, _set_bounds) - + def set_points(self, points): + self._points = points + self.invalidate() + + def set(self, other): + self._points = other.get_points() + self.invalidate() + def transformed(self, transform): - return Bbox(transform(self._points)) + return Bbox(transform(self._points)) def inverse_transformed(self, transform): - return Bbox(transform.inverted()(self._points)) + return Bbox(transform.inverted()(self._points)) def expanded(self, sw, 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) + 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 contains(self, x, y): - return (x >= self.xmin and x <= self.xmax and - y >= self.ymin and y <= self.ymax) - #@staticmethod def union(bboxes): - """ - Return the Bbox that bounds all bboxes - """ - assert(len(bboxes)) + """ + Return the Bbox that bounds all bboxes + """ + assert(len(bboxes)) - if len(bboxes) == 1: - return bboxes[0] + if len(bboxes) == 1: + return bboxes[0] - bbox = bboxes[0] - xmin = bbox.xmin - ymin = bbox.ymin - xmax = bbox.xmax - ymax = bbox.ymax + bbox = bboxes[0] + xmin = bbox.xmin + ymin = bbox.ymin + xmax = bbox.xmax + ymax = bbox.ymax - for bbox in bboxes[1:]: - xmin = min(xmin, bbox.xmin) - ymin = min(ymin, bbox.ymin) - xmax = max(xmax, bbox.xmax) - ymax = max(ymax, bbox.ymax) + for bbox in bboxes[1:]: + xmin = min(xmin, bbox.xmin) + ymin = min(ymin, bbox.ymin) + xmax = max(xmax, bbox.xmax) + ymax = max(ymax, bbox.ymax) - return Bbox.from_lbrt(xmin, ymin, xmax, ymax) + return Bbox.from_lbrt(xmin, ymin, xmax, ymax) union = staticmethod(union) + +class TransformedBbox(BboxBase): + def __init__(self, bbox, transform): + assert isinstance(bbox, Bbox) + assert isinstance(transform, Transform) + + BboxBase.__init__(self) + self.bbox = bbox + self.transform = transform + self.set_children(['bbox', 'transform']) + 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: + 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) + TransformNode.__init__(self) def __call__(self, points): - raise NotImplementedError() + raise NotImplementedError() def __add__(self, other): - if isinstance(other, Transform): - return composite_transform_factory(self, other) - raise TypeError("Can not add Transform to object of type '%s'" % type(other)) + if isinstance(other, Transform): + return composite_transform_factory(self, other) + raise TypeError( + "Can not add Transform to object of type '%s'" % type(other)) def __radd__(self, other): - if isinstance(other, Transform): - return composite_transform_factory(other, self) - raise TypeError("Can not add Transform to object of type '%s'" % type(other)) + if isinstance(other, Transform): + return composite_transform_factory(other, self) + raise TypeError( + "Can not add Transform to object of type '%s'" % type(other)) def transform_point(self, point): - return self.__call__(npy.asarray([point]))[0] + return self.__call__(npy.asarray([point]))[0] def has_inverse(self): - raise NotImplementedError() + raise NotImplementedError() def inverted(self): - raise NotImplementedError() + raise NotImplementedError() def is_separable(self): - return False + return False def is_affine(self): - return False + return False class Affine2DBase(Transform): input_dims = 2 output_dims = 2 def __init__(self): - Transform.__init__(self) - self._inverted = None + Transform.__init__(self) + self._inverted = None def _do_invalidation(self): - result = self._inverted is None - self._inverted = None - return result + result = self._inverted is None + self._inverted = None + return result #@staticmethod def _concat(a, b): return npy.dot(b, a) _concat = staticmethod(_concat) + + #@staticmethod + def concat(a, b): + return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix())) + concat = staticmethod(concat) def to_values(self): - mtx = self.get_matrix() - return tuple(mtx[:2].swapaxes(0, 1).flatten()) + mtx = self.get_matrix() + return tuple(mtx[:2].swapaxes(0, 1).flatten()) #@staticmethod def matrix_from_values(a, b, c, d, e, f): - affine = npy.zeros((3,3), npy.float_) - affine[0,] = a, c, e - affine[1,] = b, d, f - affine[2,2] = 1 - return affine + affine = npy.zeros((3, 3), npy.float_) + affine[0, ] = a, c, e + affine[1, ] = b, d, f + affine[2, 2] = 1 + return affine matrix_from_values = staticmethod(matrix_from_values) def get_matrix(self): - raise NotImplementedError() + raise NotImplementedError() def __call__(self, points): """ Applies the transformation to an array of 2D points and - returns the result. + returns the result. - points must be a numpy array of shape (N, 2), where N is the - number of points. - """ - # MGDTODO: The major speed trap here is just converting to - # the points to an array in the first place. If we can use - # more arrays upstream, that should help here. - mtx = self.get_matrix() - points = npy.asarray(points, npy.float_) - points = points.transpose() - points = npy.dot(mtx[0:2, 0:2], points) - points = points + mtx[0:2, 2:] - return points.transpose() + points must be a numpy array of shape (N, 2), where N is the + number of points. + """ + # MGDTODO: The major speed trap here is just converting to + # the points to an array in the first place. If we can use + # more arrays upstream, that should help here. + if not isinstance(points, npy.ndarray): + import traceback + print '-' * 60 + print 'A non-numpy array was passed in for transformation. Please ' + print 'correct this.' + print "".join(traceback.format_stack()) + print points + mtx = self.get_matrix() + points = npy.asarray(points, npy.float_) + points = points.transpose() + points = npy.dot(mtx[0:2, 0:2], points) + points = points + mtx[0:2, 2:] + return points.transpose() def inverted(self): - if self._inverted is None: - mtx = self.get_matrix() - self._inverted = Affine2D(inv(mtx)) - return self._inverted + if self._inverted is None: + mtx = self.get_matrix() + self._inverted = Affine2D(inv(mtx)) + return self._inverted def is_separable(self): - mtx = self.get_matrix() - return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 + mtx = self.get_matrix() + return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 def is_affine(self): - return True + return True - + class Affine2D(Affine2DBase): input_dims = 2 output_dims = 2 @@ -310,29 +487,29 @@ b d f 0 0 1 """ - Affine2DBase.__init__(self) - if matrix is None: - matrix = npy.identity(3) - else: - assert matrix.shape == (3, 3) - self._mtx = matrix - self._inverted = None + Affine2DBase.__init__(self) + if matrix is None: + matrix = npy.identity(3) + else: + assert matrix.shape == (3, 3) + self._mtx = matrix + self._inverted = None def __repr__(self): - return "Affine2D(%s)" % repr(self._mtx) + return "Affine2D(%s)" % repr(self._mtx) __str__ = __repr__ def __cmp__(self, other): - if (isinstance(other, Affine2D) and - (self.get_matrix() == other.get_matrix()).all()): - return 0 - return -1 + if (isinstance(other, Affine2D) and + (self.get_matrix() == other.get_matrix()).all()): + return 0 + return -1 def __copy__(self): - return Affine2D(self._mtx.copy()) + return Affine2D(self._mtx.copy()) def __deepcopy__(self, memo): - return Affine2D(self._mtx.copy()) + return Affine2D(self._mtx.copy()) #@staticmethod def from_values(a, b, c, d, e, f): @@ -340,209 +517,224 @@ from_values = staticmethod(from_values) def get_matrix(self): - return self._mtx + return self._mtx + + def set_matrix(self, mtx): + self._mtx = mtx + self.invalidate() + + def set(self, other): + self._mtx = other.get_matrix() + self.invalidate() #@staticmethod - def concat(a, b): - return Affine2D(Affine2D._concat(a._mtx, b._mtx)) - concat = staticmethod(concat) - - #@staticmethod def identity(): return Affine2D(npy.identity(3)) identity = staticmethod(identity) + def clear(self): + self._mtx = npy.identity(3) + self.invalidate() + return self + def rotate(self, theta): a = npy.cos(theta) b = npy.sin(theta) rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0) self._mtx = self._concat(self._mtx, rotate_mtx) - self.invalidate() - return self + self.invalidate() + return self def rotate_deg(self, degrees): return self.rotate(degrees*npy.pi/180.) + def rotate_around(self, x, y, theta): + return self.translate(-x, -y).rotate(theta).translate(x, y) + + def rotate_deg_around(self, x, y, degrees): + return self.translate(-x, -y).rotate_deg(degrees).translate(x, y) + def translate(self, tx, ty): translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty) self._mtx = self._concat(self._mtx, translate_mtx) - self.invalidate() - return self + self.invalidate() + return self def scale(self, sx, sy=None): - if sy is None: - sy = sx - scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.) + if sy is None: + sy = sx + scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.) self._mtx = self._concat(self._mtx, scale_mtx) - self.invalidate() - return self + self.invalidate() + return self def inverted(self): - if self._inverted is None: - mtx = self.get_matrix() - self._inverted = Affine2D(inv(mtx)) - return self._inverted + if self._inverted is None: + mtx = self.get_matrix() + self._inverted = Affine2D(inv(mtx)) + return self._inverted def is_separable(self): - mtx = self.get_matrix() - return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 + mtx = self.get_matrix() + return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 def is_affine(self): - return True + return True -class BlendedAffine2D(Affine2DBase): +class BlendedTransform(Transform): def __init__(self, x_transform, y_transform): - assert x_transform.is_affine() - assert y_transform.is_affine() - assert x_transform.is_separable() - assert y_transform.is_separable() + assert x_transform.is_separable() + assert y_transform.is_separable() - Affine2DBase.__init__(self) - self.add_children([x_transform, y_transform]) - self._x = x_transform - self._y = y_transform - self._mtx = None + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) + def __call__(self, points): + if self._x == self._y: + return self._x(points) + + x_points = self._x(points) + y_points = self._y(points) + # This works because we already know the transforms are + # separable + return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) + +# def set_x_transform(self, x_transform): +# self.replace_child(0, x_transform) + +# def set_y_transform(self, y_transform): +# self.replace_child(1, y_transform) + +class BlendedAffine2D(Affine2DBase, BlendedTransform): + def __init__(self, x_transform, y_transform): + assert x_transform.is_affine() + assert y_transform.is_affine() + assert x_transform.is_separable() + assert y_transform.is_separable() + BlendedTransform.__init__(self, x_transform, y_transform) + + Affine2DBase.__init__(self) + self._mtx = None + def __repr__(self): - return "BlendedAffine2D(%s,%s)" % (self._x, self._y) + return "BlendedAffine2D(%s,%s)" % (self._x, self._y) __str__ = __repr__ - + def _do_invalidation(self): - if self._mtx is not None: - self._mtx = None - Affine2DBase._do_invalidation(self) - return False - return True + if self._mtx is not None: + self._mtx = None + Affine2DBase._do_invalidation(self) + return False + return True - def _make__mtx(self): - if self._mtx is None: - x_mtx = self._x.get_matrix() - y_mtx = self._y.get_matrix() - # This works because we already know the transforms are - # separable, though normally one would want to set b and - # c to zero. - self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) - def is_separable(self): - return True + return True def get_matrix(self): - self._make__mtx() - return self._mtx + if self._mtx is None: + if self._x == self._y: + self._mtx = self._x.get_matrix() + else: + x_mtx = self._x.get_matrix() + y_mtx = self._y.get_matrix() + # This works because we already know the transforms are + # separable, though normally one would want to set b and + # c to zero. + self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) + return self._mtx + +class CompositeTransform(Transform): + def __init__(self, a, b): + assert a.output_dims == b.input_dims + self.input_dims = a.input_dims + self.output_dims = b.output_dims + + Transform.__init__(self) + self._a = a + self._b = b + self.set_children(['_a', '_b']) + + def __call__(self, points): + return self._b(self._a(points)) -class BlendedTransform(Transform): - def __init__(self, x_transform, y_transform): - assert x_transform.is_separable() - assert y_transform.is_separable() - - Transform.__init__(self) - self.add_children([x_transform, y_transform]) - self._x = x_transform - self._y = y_transform - - def __call__(self, points): - x_points = self._x(points) - y_points = self._y(points) - # This works because we already know the transforms are - # separable - return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) - class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): - assert a.is_affine() - assert b.is_affine() + assert a.is_affine() + assert b.is_affine() - Affine2DBase.__init__(self) - self.add_children([a, b]) - self._a = a - self._b = b - self._mtx = None + Affine2DBase.__init__(self) + self._a = a + self._b = b + self.set_children(['_a', '_b']) + self._mtx = None def __repr__(self): - return "CompositeAffine2D(%s, %s)" % (self._a, self._b) + return "CompositeAffine2D(%s, %s)" % (self._a, self._b) __str__ = __repr__ def _do_invalidation(self): - self._mtx = None - Affine2DBase._do_invalidation(self) + self._mtx = None + Affine2DBase._do_invalidation(self) - def _make__mtx(self): - if self._mtx is None: - self._mtx = self._concat( - self._a.get_matrix(), - self._b.get_matrix()) - def get_matrix(self): - self._make__mtx() - return self._mtx - -class CompositeTransform(Transform): - def __init__(self, a, b): - assert a.output_dims == b.input_dims - self.input_dims = a.input_dims - self.output_dims = b.output_dims - - Transform.__init__(self) - self.add_children([a, b]) - self._a = a - self._b = b + if self._mtx is None: + self._mtx = self._concat( + self._a.get_matrix(), + self._b.get_matrix()) + return self._mtx - def __call__(self, points): - return self._b(self._a(points)) - class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): - assert isinstance(boxin, Bbox) - assert isinstance(boxout, Bbox) + assert isinstance(boxin, BboxBase) + assert isinstance(boxout, BboxBase) - Affine2DBase.__init__(self) - self.add_children([boxin, boxout]) - self._boxin = boxin - self._boxout = boxout - self._mtx = None - self._inverted = None + Affine2DBase.__init__(self) + self._boxin = boxin + self._boxout = boxout + self.set_children(['_boxin', '_boxout']) + self._mtx = None + self._inverted = None def __repr__(self): - return "BboxTransform(%s, %s)" % (self._boxin, self._boxout) + return "BboxTransform(%s, %s)" % (self._boxin, self._boxout) __str__ = __repr__ - + def _do_invalidation(self): - if self._mtx is not None: - self._mtx = None - Affine2DBase._do_invalidation(self) - return False - return True + if self._mtx is not None: + self._mtx = None + Affine2DBase._do_invalidation(self) + return False + return True - def _make__mtx(self): - if self._mtx is None: - boxin = self._boxin - boxout = self._boxout - x_scale = boxout.width / boxin.width - y_scale = boxout.height / boxin.height + def is_separable(self): + return True - # MGDTODO: Optimize - affine = Affine2D() \ - .translate(-boxin.xmin, -boxin.ymin) \ - .scale(x_scale, y_scale) \ - .translate(boxout.xmin, boxout.ymin) + def get_matrix(self): + if self._mtx is None: + boxin = self._boxin + boxout = self._boxout + x_scale = boxout.width / boxin.width + y_scale = boxout.height / boxin.height - self._mtx = affine._mtx - - def is_separable(self): - return True + # MGDTODO: Optimize + affine = Affine2D() \ + .translate(-boxin.xmin, -boxin.ymin) \ + .scale(x_scale, y_scale) \ + .translate(boxout.xmin, boxout.ymin) - def get_matrix(self): - self._make__mtx() - return self._mtx - + self._mtx = affine._mtx + return self._mtx + def blend_xy_sep_transform(x_transform, y_transform): if x_transform.is_affine() and y_transform.is_affine(): - return BlendedAffine2D(x_transform, y_transform) + return BlendedAffine2D(x_transform, y_transform) return BlendedTransform(x_transform, y_transform) def composite_transform_factory(a, b): if a.is_affine() and b.is_affine(): - return CompositeAffine2D(a, b) + return CompositeAffine2D(a, b) return CompositeTransform(a, b) # MGDTODO: There's probably a better place for this @@ -562,7 +754,7 @@ vmin, vmax = vmax, vmin swapped = True if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin==0.0: + if vmin == 0.0: vmin = -expander vmax = expander else: @@ -635,8 +827,8 @@ assert scale.to_values() == (10, 0, 0, 20, 0, 0) rotation = Affine2D().rotate_deg(30) print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, - -0.49999999999999994, 0.86602540378443871, - 0.0, 0.0) + -0.49999999999999994, 0.86602540378443871, + 0.0, 0.0) points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_) translated_points = translation(points) Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/artist.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -21,7 +21,7 @@ # http://groups.google.com/groups?hl=en&lr=&threadm=mailman.5090.1098044946.5135.python-list%40python.org&rnum=1&prev=/groups%3Fq%3D__doc__%2Bauthor%253Ajdhunter%2540ace.bsd.uchicago.edu%26hl%3Den%26btnG%3DGoogle%2BSearch -class Artist: +class Artist(object): """ Abstract base class for someone who renders into a FigureCanvas """ Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -436,8 +436,7 @@ } def __str__(self): - return "Axes(%g,%g;%gx%g)"%(self._position[0].get(),self._position[1].get(), - self._position[2].get(),self._position[3].get()) + return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds) def __init__(self, fig, rect, axisbg = None, # defaults to rc axes.facecolor frameon = True, @@ -590,13 +589,13 @@ def follow_foreign_ylim(ax): ymin, ymax = axforeign.get_ylim() - # do not emit here or we'll get a ping png effect + # do not emit here or we'll get a ping pong effect self.set_ylim(ymin, ymax, emit=False) self.figure.canvas.draw_idle() def follow_self_ylim(ax): ymin, ymax = self.get_ylim() - # do not emit here or we'll get a ping png effect + # do not emit here or we'll get a ping pong effect axforeign.set_ylim(ymin, ymax, emit=False) axforeign.figure.canvas.draw_idle() @@ -613,65 +612,66 @@ """ martist.Artist.set_figure(self, fig) - l, b, w, h = self._position.bounds - xmin = fig.bbox.xmin - xmax = fig.bbox.xmax - ymin = fig.bbox.ymin - ymax = fig.bbox.ymax - figw = xmax-xmin - figh = ymax-ymin - self.left = l*figw - self.bottom = b*figh - self.right = (l+w)*figw - self.top = (b+h)*figh - - self.bbox = maffine.Bbox.from_lbrt( - self.left, self.bottom, - self.right, self.top, - ) + self.bbox = maffine.TransformedBbox(self._position, fig.transFigure) #these will be updated later as data is added self._set_lim_and_transforms() + def _shared_xlim_callback(self, ax): + xmin, xmax = ax.get_xlim() + self.set_xlim(xmin, xmax, emit=False) + self.figure.canvas.draw_idle() + + def _shared_ylim_callback(self, ax): + ymin, ymax = ax.get_ylim() + self.set_ylim(ymin, ymax, emit=False) + self.figure.canvas.draw_idle() + def _set_lim_and_transforms(self): """ set the dataLim and viewLim BBox attributes and the transData and transAxes Transformation attributes """ - Bbox = maffine.Bbox + Bbox = maffine.Bbox + self.viewLim = Bbox.unit() + if self._sharex is not None: - left = self._sharex.viewLim.xmin() - right = self._sharex.viewLim.xmax() - else: - left = 0.0 - right = 1.0 + # MGDTODO: This may be doing at least one too many updates + # than necessary + self._sharex.callbacks.connect( + 'xlim_changed', self._shared_xlim_callback) + self.viewLim.intervalx = self._sharex.viewLim.intervalx if self._sharey is not None: - bottom = self._sharey.viewLim.ymin() - top = self._sharey.viewLim.ymax() - else: - bottom = 0.0 - top = 1.0 + self._sharey.callbacks.connect( + 'ylim_changed', self._shared_ylim_callback) + self.viewLim.intervaly = self._sharex.viewLim.intervaly - self.viewLim = Bbox.from_lbrt(left, bottom, right, top) self.dataLim = Bbox.unit() - self.transData = maffine.BboxTransform( - self.viewLim, self.bbox) self.transAxes = maffine.BboxTransform( Bbox.unit(), self.bbox) - # MGDTODO -# if self._sharex: -# self.transData.set_funcx(self._sharex.transData.get_funcx()) - -# if self._sharey: -# self.transData.set_funcy(self._sharey.transData.get_funcy()) - + localTransData = maffine.BboxTransform( + self.viewLim, self.bbox) + if self._sharex: + transDataX = self._sharex.transData + else: + transDataX = localTransData + if self._sharey: + transDataY = self._sharey.transData + else: + transDataY = localTransData + self.transData = localTransData # maffine.blend_xy_sep_transform(transDataX, transDataY) + + def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' + # MGDTODO: This changed from returning a list to returning a Bbox + # If you get any errors with the result of this function, please + # update the calling code if original: - return self._originalPosition.bounds + return copy.copy(self._originalPosition) else: - return self._position.bounds + return copy.copy(self._position) # return [val.get() for val in self._position] def set_position(self, pos, which='both'): @@ -690,14 +690,9 @@ ACCEPTS: len(4) sequence of floats """ if which in ('both', 'active'): - # MGDTODO -# # Change values within self._position--don't replace it. -# for num,val in zip(pos, self._position): -# val.set(num) - self._position.bounds = pos.bounds - # MGDTODO: side-effects + self._position.set(pos) if which in ('both', 'original'): - self._originalPosition.bounds = pos.bounds + self._originalPosition.set(pos) def _set_artist_props(self, a): @@ -1546,7 +1541,14 @@ xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False) self.viewLim.intervalx = (xmin, xmax) - + if emit: + self.callbacks.process('xlim_changed', self) + # MGDTODO: It would be nice to do this is in the above callback list, + # but it's difficult to tell how to initialize this at the + # right time + if self._sharex: + self._sharex.set_xlim(*self.viewLim.intervalx) + return xmin, xmax def get_xscale(self): @@ -1650,7 +1652,6 @@ ACCEPTS: len(2) sequence of floats """ - if ymax is None and iterable(ymin): ymin,ymax = ymin @@ -1671,8 +1672,14 @@ ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False) self.viewLim.intervaly = (ymin, ymax) - if emit: self.callbacks.process('ylim_changed', self) - + if emit: + self.callbacks.process('ylim_changed', self) + # MGDTODO: It would be nice to do this is in the above callback list, + # but it's difficult to tell how to initialize this at the + # right time + if self._sharey: + self._sharey.set_ylim(*self.viewLim.intervaly) + return ymin, ymax def get_yscale(self): Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/axis.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -1028,9 +1028,8 @@ x,y = self.label.get_position() if self.label_position == 'bottom': if not len(bboxes): - bottom = self.axes.bbox.ymin() + bottom = self.axes.bbox.ymin else: - bbox = Bbox.union(bboxes) bottom = bbox.ymin @@ -1041,7 +1040,6 @@ if not len(bboxes2): top = self.axes.bbox.ymax else: - bbox = bbox_union(bboxes2) top = bbox.ymax @@ -1054,7 +1052,7 @@ """ x,y = self.offsetText.get_position() if not len(bboxes): - bottom = self.axes.bbox.ymin() + bottom = self.axes.bbox.ymin else: bbox = Bbox.union(bboxes) bottom = bbox.ymin Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -752,7 +752,8 @@ else: # Just found one hit self.inaxes = axes_list[0] - try: xdata, ydata = self.inaxes.transData.inverted()([[x, y]])[0] + try: + xdata, ydata = self.inaxes.transData.inverted().transform_point((x, y)) except ValueError: self.xdata = None self.ydata = None @@ -1584,8 +1585,8 @@ lims.append( (xmin, xmax, ymin, ymax) ) # Store both the original and modified positions pos.append( ( - tuple( a.get_position(True) ), - tuple( a.get_position() ) ) ) + a.get_position(True), + a.get_position() ) ) self._views.push(lims) self._positions.push(pos) self.set_history_buttons() Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/figure.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -18,7 +18,7 @@ from text import Text, _process_text_args from legend import Legend -from affine import Bbox, BboxTransform +from affine import Affine2D, Bbox, BboxTransform, TransformedBbox from ticker import FormatStrFormatter from cm import ScalarMappable from contour import ContourSet @@ -128,17 +128,15 @@ if facecolor is None: facecolor = rcParams['figure.facecolor'] if edgecolor is None: edgecolor = rcParams['figure.edgecolor'] + self._dpi_scale_trans = Affine2D() self.dpi = dpi - figwidth = figsize[0] * dpi - figheight = figsize[1] * dpi - self.bbox = Bbox.from_lbwh(0, 0, figwidth, figheight) + self.bbox_inches = Bbox.from_lbwh(0, 0, *figsize) + self.bbox = TransformedBbox(self.bbox_inches, self._dpi_scale_trans) self.frameon = frameon - self.transFigure = BboxTransform( Bbox.unit(), self.bbox) + self.transFigure = BboxTransform(Bbox.unit(), self.bbox) - - self.figurePatch = Rectangle( xy=(0,0), width=1, height=1, facecolor=facecolor, edgecolor=edgecolor, @@ -160,6 +158,15 @@ self._cachedRenderer = None + def _get_dpi(self): + return self._dpi + def _set_dpi(self, dpi): + print "setting dpi" + self._dpi = dpi + self._dpi_scale_trans.clear().scale(dpi, dpi) + print self._dpi_scale_trans + dpi = property(_get_dpi, _set_dpi) + def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'): """ A common use case is a number of subplots with shared xaxes @@ -325,7 +332,7 @@ w,h = args dpival = self.dpi - self.bbox.max = w * dpival, h * dpival + self.bbox_inches.max = w, h # self.figwidth.set(w) MGDTODO # self.figheight.set(h) @@ -339,7 +346,7 @@ manager.resize(int(canvasw), int(canvash)) def get_size_inches(self): - return self.bbox.max + return self.bbox_inches.max # return self.figwidth.get(), self.figheight.get() MGDTODO def get_edgecolor(self): @@ -352,12 +359,12 @@ def get_figwidth(self): 'Return the figwidth as a float' - return self.bbox.xmax + return self.bbox_inches.xmax # return self.figwidth.get() MGDTODO def get_figheight(self): 'Return the figheight as a float' - return self.bbox.ymax + return self.bbox_inches.ymax def get_dpi(self): 'Return the dpi as a float' @@ -400,7 +407,7 @@ ACCEPTS: float """ # self.figwidth.set(val) MGDTODO - self.bbox.xmax = val + self.bbox_inches.xmax = val def set_figheight(self, val): """ @@ -409,7 +416,7 @@ ACCEPTS: float """ # MGDTODO (set()) - self.bbox.ymax = val + self.bbox_inches.ymax = val def set_frameon(self, b): """ Modified: branches/transforms/lib/matplotlib/pyplot.py =================================================================== --- branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -481,7 +481,7 @@ byebye = [] for other in fig.axes: if other==a: continue - if bbox.overlaps(other.bbox, ignoreend=True): + if bbox.fully_overlaps(other.bbox): byebye.append(other) for ax in byebye: delaxes(ax) Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-09-13 12:50:05 UTC (rev 3847) +++ branches/transforms/lib/matplotlib/text.py 2007-09-13 18:00:10 UTC (rev 3848) @@ -15,7 +15,7 @@ from cbook import enumerate, is_string_like, maxdict, is_numlike from font_manager import FontProperties from patches import bbox_artist, YAArrow -from affine import Bbox +from affine import Affine2D, Bbox from lines import Line2D import matplotlib.nxutils as nxutils @@ -213,30 +213,32 @@ M = self.get_rotation_matrix(xmin, ymin) # the corners of the unrotated bounding box - cornersHoriz = ( (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) ) - offsetLayout = [] + cornersHoriz = npy.array( + [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)], + npy.float_) + offsetLayout = npy.zeros((len(lines), 2)) # now offset the individual text lines within the box if len(lines)>1: # do the multiline aligment malign = self._get_multialignment() - for line, thisx, thisy, w, h in horizLayout: + 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.append( (thisx, thisy )) + offsetLayout[i] = (thisx, thisy) else: # no additional layout needed - offsetLayout = [ (thisx, thisy) for line, thisx, thisy, w, h in horizLayout] + offsetLayout[0] = horizLayout[0][1:3] # now rotate the bbox - cornersRotated = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in cornersHoriz] + cornersRotated = M(cornersHoriz) - txs = [float(v[0][0]) for v in cornersRotated] - tys = [float(v[1][0]) for v in cornersRotated] + txs = cornersRotated[:, 0] + tys = cornersRotated[:, 1] # compute the bounds of the rotated box - xmin, xmax = min(txs), max(txs) - ymin, ymax = min(tys), max(tys) + xmin, xmax = txs.min(), txs.max() + ymin, ymax = tys.min(), tys.max() width = xmax - xmin height = ymax - ymin @@ -264,17 +266,18 @@ bbox = Bbox.from_lbwh(xmin, ymin, width, height) + # now rotate the positions around the first x,y position - xys = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in offsetLayout] + xys = M(offsetLayout) + tx = xys[:, 0] + ty = xys[:, 1] + tx += offsetx + ty += offsety - - tx = [float(v[0][0])+offsetx for v in xys] - ty = [float(v[1][0])+offsety for v in xys] - # now inverse transform back to data coords inverse_transform = self.get_transform().inverted() - xys = inverse_transform(zip(tx, ty)) + xys = inverse_transform(xys) xs, ys = zip(*xys) @@ -327,7 +330,7 @@ return for line, wh, x, y in info: - x, y = trans([[x, y]])[0] + x, y = trans.transform_point((x, y)) if renderer.flipy(): canvasw, canvash = renderer.get_canvas_width_height() @@ -435,29 +438,9 @@ bbox, info = self._get_layout(self._renderer) return bbox - - def get_rotation_matrix(self, x0, y0): + return Affine2D().rotate_deg_around(x0, y0, self.get_rotation()) - theta = npy.pi/180.0*self.get_rotation() - # translate x0,y0 to origin - Torigin = npy.array([ [1, 0, -x0], - [0, 1, -y0], - [0, 0, 1 ]]) - - # rotate by theta - R = npy.array([ [npy.cos(theta), -npy.sin(theta), 0], - [npy.sin(theta), npy.cos(theta), 0], - [0, 0, 1]]) - - # translate origin back to x0,y0 - Tback = npy.array([ [1, 0, x0], - [0, 1, y0], - [0, 0, 1 ]]) - - - return npy.dot(npy.dot(Tback,R), Torigin) - def set_backgroundcolor(self, color): """ Set the background color of the text by updating the bbox (see set_bbox for more info) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-13 12:50:07
|
Revision: 3847 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3847&view=rev Author: mdboom Date: 2007-09-13 05:50:05 -0700 (Thu, 13 Sep 2007) Log Message: ----------- Merged revisions 3836-3846 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r3844 | jdh2358 | 2007-09-12 16:37:41 -0400 (Wed, 12 Sep 2007) | 1 line fixed a bar units bug ........ r3845 | jouni | 2007-09-13 02:29:14 -0400 (Thu, 13 Sep 2007) | 3 lines More work on dviread and usetex in pdf. It is more usable now, so I am renaming the method from _draw_tex to draw_tex. ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CHANGELOG branches/transforms/lib/matplotlib/artist.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backends/backend_pdf.py branches/transforms/lib/matplotlib/dviread.py branches/transforms/lib/matplotlib/mlab.py branches/transforms/lib/matplotlib/patches.py Added Paths: ----------- branches/transforms/examples/units/bar_demo2.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-3835 + /trunk/matplotlib:1-3846 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/API_CHANGES 2007-09-13 12:50:05 UTC (rev 3847) @@ -1,3 +1,5 @@ + Made skiprows=1 the default on csv2rec + The gd and paint backends have been deleted. The errorbar method and function now accept additional kwargs Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/CHANGELOG 2007-09-13 12:50:05 UTC (rev 3847) @@ -1,3 +1,10 @@ +2007-09-13 The usetex support in the pdf backend is more usable now, + so I am enabling it. - JKS + +2007-09-12 Fixed a Axes.bar unit bug - JDH + +2007-09-10 Made skiprows=1 the default on csv2rec - JDH + 2007-09-09 Split out the plotting part of pylab and put it in pyplot.py; removed numerix from the remaining pylab.py, which imports everything from pyplot.py. The intention Copied: branches/transforms/examples/units/bar_demo2.py (from rev 3845, trunk/matplotlib/examples/units/bar_demo2.py) =================================================================== --- branches/transforms/examples/units/bar_demo2.py (rev 0) +++ branches/transforms/examples/units/bar_demo2.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -0,0 +1,34 @@ +""" +plot using a variety of cm vs inches conversions. The example shows +how default unit instrospection works (ax1), how various keywords can +be used to set the x and y units to override the defaults (ax2, ax3, +ax4) and how one can set the xlimits using scalars (ax3, current units +assumed) or units (conversions applied to get the numbers to current +units) + +""" +from basic_units import cm, inch +from pylab import figure, show, nx + +cms = cm *nx.arange(0, 10, 2) +bottom=0*cm +width=0.8*cm + +fig = figure() + +ax1 = fig.add_subplot(2,2,1) +ax1.bar(cms, cms, bottom=bottom) + +ax2 = fig.add_subplot(2,2,2) +ax2.bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch) + +ax3 = fig.add_subplot(2,2,3) +ax3.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm) +ax3.set_xlim(3, 6) # scalars are interpreted in current units + +ax4 = fig.add_subplot(2,2,4) +ax4.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch) +#fig.savefig('simple_conversion_plot.png') +ax4.set_xlim(3*cm, 6*cm) # cm are converted to inches + +show() Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -51,7 +51,7 @@ self._remove_method = None def remove(self): - ''' + """ Remove the artist from the figure if possible. The effect will not be visible until the figure is redrawn, e.g., with ax.draw_idle(). Call ax.relim() to update the axes limits if desired. @@ -60,7 +60,7 @@ was added to axes with autolim=True. Note: there is no support for removing the artist's legend entry. - ''' + """ # There is no method to set the callback. Instead the parent should set # the _remove_method attribute directly. This would be a protected Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -1193,23 +1193,27 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None): 'look for unit kwargs and update the axis instances as necessary' - if self.xaxis is None or self.xaxis is None: return + if self.xaxis is None or self.yaxis is None: return - + #print 'processing', self.get_geometry() if xdata is not None: self.xaxis.update_units(xdata) + #print '\tset from xdata', self.xaxis.units if ydata is not None: self.yaxis.update_units(ydata) + #print '\tset from ydata', self.yaxis.units # process kwargs 2nd since these will override default units if kwargs is not None: xunits = kwargs.pop( 'xunits', self.xaxis.units) if xunits!=self.xaxis.units: + #print '\tkw setting xunits', xunits self.xaxis.set_units(xunits) yunits = kwargs.pop('yunits', self.yaxis.units) if yunits!=self.yaxis.units: + #print '\tkw setting yunits', yunits self.yaxis.set_units(yunits) def in_axes(self, xwin, ywin): @@ -3122,11 +3126,13 @@ else: raise ValueError, 'invalid orientation: %s' % orientation - left = npy.asarray(left) - height = npy.asarray(height) - width = npy.asarray(width) - bottom = npy.asarray(bottom) + # do not convert to array here as unit info is lost + #left = npy.asarray(left) + #height = npy.asarray(height) + #width = npy.asarray(width) + #bottom = npy.asarray(bottom) + if len(linewidth) == 1: linewidth = linewidth * nbars # if color looks like a color string, an RGB tuple or a @@ -3169,14 +3175,14 @@ # lets do some conversions now if self.xaxis is not None: xconv = self.xaxis.converter - if ( xconv ): + if xconv is not None: units = self.xaxis.get_units() left = xconv.convert( left, units ) width = xconv.convert( width, units ) if self.yaxis is not None: yconv = self.yaxis.converter - if ( yconv ): + if yconv is not None : units = self.yaxis.get_units() bottom = yconv.convert( bottom, units ) height = yconv.convert( height, units ) @@ -3216,12 +3222,14 @@ if xerr is not None or yerr is not None: if orientation == 'vertical': - x = left + 0.5*width - y = bottom + height + # using list comps rather than arrays to preserve unit info + x = [l+0.5*w for l, w in zip(left, width)] + y = [b+h for b,h in zip(bottom, height)] elif orientation == 'horizontal': - x = left + width - y = bottom + 0.5*height + # using list comps rather than arrays to preserve unit info + x = [l+w for l,w in zip(left, width)] + y = [b+0.5*h for b,h in zip(bottom, height)] self.errorbar( x, y, Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -821,7 +821,7 @@ return x ret = self.converter.convert(x, self.units) - #print 'convert_units converting: units=%s, converter=%s, in=%s, out=%s'%(self.units, self.converter, x, ret) + #print 'convert_units converting: axis=%s, units=%s, converter=%s, in=%s, out=%s'%(self, self.units, self.converter, x, ret) return ret def set_units(self, u): Modified: branches/transforms/lib/matplotlib/backends/backend_pdf.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -527,7 +527,7 @@ widths.append(afmdata.get_width_from_char_name(ch)) except KeyError: matplotlib.verbose.report( - 'No width for %s in %s' % (ch, fullname), 'debug') + 'No width for %s in %s' % (ch, fullname), 'debug-annoying') widths.append(0) differencesArray = [ Name(ch) for ch in enc ] @@ -561,7 +561,7 @@ except KeyError: matplotlib.verbose.report( 'No name for glyph %d in %s' % (ch, fullname), - 'debug') + 'debug-annoying') need_idx = True @@ -1449,9 +1449,7 @@ # Pop off the global transformation self.file.output(Op.grestore) - def _draw_tex(self, gc, x, y, s, prop, angle): - # Rename to draw_tex to enable - + def draw_tex(self, gc, x, y, s, prop, angle): texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() dvifile = texmanager.make_dvi(s, fontsize) @@ -1494,7 +1492,7 @@ elt[3][-1] += next[3][0] elt[4] += next[4]-next[1] else: - elt[3] += [offset, next[3][0]] + elt[3] += [offset*1000.0/dvifont.size, next[3][0]] elt[4] = next[4] del seq[i+1] continue Modified: branches/transforms/lib/matplotlib/dviread.py =================================================================== --- branches/transforms/lib/matplotlib/dviread.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/dviread.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -84,16 +84,22 @@ e = 0 # zero depth else: # glyph x,y,font,g,w = elt - h = (font.scale * font.tfm.height[g]) >> 20 - e = (font.scale * font.tfm.depth[g]) >> 20 + h = _mul2012(font._scale, font._tfm.height[g]) + e = _mul2012(font._scale, font._tfm.depth[g]) minx = min(minx, x) miny = min(miny, y - h) maxx = max(maxx, x + w) maxy = max(maxy, y + e) maxy_pure = max(maxy_pure, y) + if self.dpi is None: + # special case for ease of debugging: output raw dvi coordinates + return mpl_cbook.Bunch(text=self.text, boxes=self.boxes, + width=maxx-minx, height=maxy_pure-miny, + descent=maxy-maxy_pure) + d = self.dpi / (72.27 * 2**16) # from TeX's "scaled points" to dpi units - text = [ ((x-minx)*d, (maxy-y)*d, DviFont(f), g, w*d) + text = [ ((x-minx)*d, (maxy-y)*d, f, g, w*d) for (x,y,f,g,w) in self.text ] boxes = [ ((x-minx)*d, (maxy-y)*d, h*d, w*d) for (x,y,h,w) in self.boxes ] @@ -110,11 +116,11 @@ while True: byte = ord(self.file.read(1)) self._dispatch(byte) - if self.state == _dvistate.inpage: - matplotlib.verbose.report( - 'Dvi._read: after %d at %f,%f' % - (byte, self.h, self.v), - 'debug-annoying') +# if self.state == _dvistate.inpage: +# matplotlib.verbose.report( +# 'Dvi._read: after %d at %f,%f' % +# (byte, self.h, self.v), +# 'debug-annoying') if byte == 140: # end of page return True if self.state == _dvistate.post_post: # end of file @@ -225,21 +231,11 @@ # I think we can assume this is constant self.state = _dvistate.outer - def _width_of(self, char, font): - width = font.tfm.width.get(char, None) - if width is not None: - return (width * font.scale) >> 20 - - matplotlib.verbose.report( - 'No width for char %d in font %s' % (char, font.name), - 'debug') - return 0 - def _set_char(self, char): if self.state != _dvistate.inpage: raise ValueError, "misplaced set_char in dvi file" self._put_char(char) - self.h += self._width_of(char, self.fonts[self.f]) + self.h += self.fonts[self.f]._width_of(char) def _set_rule(self, a, b): if self.state != _dvistate.inpage: @@ -251,20 +247,33 @@ if self.state != _dvistate.inpage: raise ValueError, "misplaced put_char in dvi file" font = self.fonts[self.f] - if font.vf is None: + if font._vf is None: self.text.append((self.h, self.v, font, char, - self._width_of(char, font))) + font._width_of(char))) +# matplotlib.verbose.report( +# 'Dvi._put_char: %d,%d %d' %(self.h, self.v, char), +# 'debug-annoying') else: - self.text.extend([(self.h + x, self.v + y, f, g, w) - for x, y, f, g, w in font.vf[char].text]) - self.boxes.extend([(self.h + x, self.v + y, a, b) - for x, y, a, b in font.vf[char].boxes]) + scale = font._scale + for x, y, f, g, w in font._vf[char].text: + newf = DviFont(scale=_mul2012(scale, f._scale), + tfm=f._tfm, texname=f.texname, vf=f._vf) + self.text.append((self.h + _mul2012(x, scale), + self.v + _mul2012(y, scale), + newf, g, newf._width_of(g))) + self.boxes.extend([(self.h + _mul2012(x, scale), + self.v + _mul2012(y, scale), + _mul2012(a, scale), _mul2012(b, scale)) + for x, y, a, b in font._vf[char].boxes]) def _put_rule(self, a, b): if self.state != _dvistate.inpage: raise ValueError, "misplaced put_rule in dvi file" if a > 0 and b > 0: self.boxes.append((self.h, self.v, a, b)) +# matplotlib.verbose.report( +# 'Dvi._put_rule: %d,%d %d,%d' % (self.h, self.v, a, b), +# 'debug-annoying') def _nop(self): pass @@ -357,7 +366,7 @@ vf = _vffile(n[-l:]) - self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n, vf=vf) + self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf) def _post(self): if self.state != _dvistate.outer: @@ -370,17 +379,20 @@ raise NotImplementedError class DviFont(object): - __slots__ = ('texname', 'size') + """ + Object that holds a font's texname and size and supports comparison. + There are also internal attributes (for use by dviread.py) that + are _not_ used for comparison. - def __init__(self, f): - """ - Object that holds a font's texname and size and supports comparison. + The size is in Adobe points (converted from TeX points). + """ + __slots__ = ('texname', 'size', '_scale', '_vf', '_tfm') - The size is in Adobe points (converted from TeX points). - """ + def __init__(self, scale, tfm, texname, vf): + self._scale, self._tfm, self.texname, self._vf = \ + scale, tfm, texname, vf # TODO: would it make more sense to have the size in dpi units? - self.texname = f.name - self.size = f.scale * (72.0 / (72.27 * 2**16)) + self.size = scale * (72.0 / (72.27 * 2**16)) def __eq__(self, other): return self.__class__ == other.__class__ and \ @@ -389,6 +401,16 @@ def __ne__(self, other): return not self.__eq__(other) + def _width_of(self, char): + width = self._tfm.width.get(char, None) + if width is not None: + return _mul2012(width, self._scale) + + matplotlib.verbose.report( + 'No width for char %d in font %s' % (char, self.texname), + 'debug') + return 0 + class Vf(Dvi): """ A virtual font (*.vf file) containing subroutines for dvi files. @@ -465,7 +487,8 @@ raise ValueError, "pre command in middle of vf file" if i != 202: raise ValueError, "Unknown vf format %d" % i - matplotlib.verbose.report('vf file comment: ' + x, 'debug') + if len(x): + matplotlib.verbose.report('vf file comment: ' + x, 'debug') self.state = _dvistate.outer # cs = checksum, ds = design size @@ -474,7 +497,7 @@ if self._first_font is None: self._first_font = k -def fix2comp(num): +def _fix2comp(num): """ Convert from two's complement to negative. """ @@ -484,6 +507,13 @@ else: return num +def _mul2012(num1, num2): + """ + Multiply two numbers in 20.12 fixed point format. + """ + # Separated into a function because >> has surprising precedence + return (num1*num2) >> 20 + class Tfm(object): """ A TeX Font Metric file. This implementation covers only the bare @@ -497,6 +527,7 @@ (this is a dict because indexing may not start from 0) height[i], depth[i]: height and depth of character #i """ + __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth') def __init__(self, filename): matplotlib.verbose.report('opening tfm file ' + filename, 'debug') @@ -525,9 +556,9 @@ [ struct.unpack('!%dI' % (len(x)/4), x) for x in (widths, heights, depths) ] for i in range(ec-bc): - self.width[bc+i] = fix2comp(widths[ord(char_info[4*i])]) - self.height[bc+i] = fix2comp(heights[ord(char_info[4*i+1]) >> 4]) - self.depth[bc+i] = fix2comp(depths[ord(char_info[4*i+1]) & 0xf]) + self.width[bc+i] = _fix2comp(widths[ord(char_info[4*i])]) + self.height[bc+i] = _fix2comp(heights[ord(char_info[4*i+1]) >> 4]) + self.depth[bc+i] = _fix2comp(depths[ord(char_info[4*i+1]) & 0xf]) class PsfontsMap(object): @@ -552,6 +583,7 @@ the pdf-related files perhaps only avoid the "Base 14" pdf fonts. But the user may have configured these files differently. """ + __slots__ = ('_font',) def __init__(self, filename): self._font = {} @@ -627,7 +659,17 @@ encoding=encoding, filename=filename) class Encoding(object): + """ + Parses a *.enc file referenced from a psfonts.map style file. + The format this class understands is a very limited subset of + PostScript. + Usage (subject to change): + for name in Encoding(filename): + whatever(name) + """ + __slots__ = ('encoding',) + def __init__(self, filename): file = open(filename, 'rt') try: @@ -694,6 +736,10 @@ return result +# With multiple text objects per figure (e.g. tick labels) we may end +# up reading the same tfm and vf files many times, so we implement a +# simple cache. TODO: is this worth making persistent? + _tfmcache = {} _vfcache = {} @@ -721,19 +767,22 @@ if __name__ == '__main__': - matplotlib.verbose.set_level('debug') - dvi = Dvi('foo.dvi', 72) + import sys + matplotlib.verbose.set_level('debug-annoying') + fname = sys.argv[1] + try: dpi = float(sys.argv[2]) + except IndexError: dpi = None + dvi = Dvi(fname, dpi) fontmap = PsfontsMap(find_tex_file('pdftex.map')) - for text,boxes in dvi: + for page in dvi: print '=== new page ===' fPrev = None - for x,y,f,c in text: - texname = dvi.fonts[f].name - print x,y,c,chr(c),texname + for x,y,f,c,w in page.text: if f != fPrev: - print 'font', texname, '=', fontmap[texname].__dict__ + print 'font', f.texname, 'scaled', f._scale/pow(2.0,20) fPrev = f - for x,y,w,h in boxes: + print x,y,c, 32 <= c < 128 and chr(c) or '.', w + for x,y,w,h in page.boxes: print x,y,'BOX',w,h Modified: branches/transforms/lib/matplotlib/mlab.py =================================================================== --- branches/transforms/lib/matplotlib/mlab.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/mlab.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -1253,9 +1253,9 @@ if r==1 or c==1: X.shape = max(r,c), if unpack: return X.transpose() - return X + else: return X -def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',', +def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',', converterd=None, names=None, missing=None): """ Load data from comma/space/tab delimited file in fname into a Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:44:16 UTC (rev 3846) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:50:05 UTC (rev 3847) @@ -77,6 +77,8 @@ if len(kwargs): artist.setp(self, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + + def contains(self, mouseevent): """Test whether the mouse event occurred in the patch. @@ -352,7 +354,6 @@ Return the vertices of the rectangle """ x, y = self.xy - left, right = self.convert_xunits((x, x + self.width)) bottom, top = self.convert_yunits((y, y + self.height)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-13 12:44:23
|
Revision: 3846 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3846&view=rev Author: mdboom Date: 2007-09-13 05:44:16 -0700 (Thu, 13 Sep 2007) Log Message: ----------- Minor changes -- committing so I can merge again. Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/text.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-13 06:29:14 UTC (rev 3845) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-13 12:44:16 UTC (rev 3846) @@ -4,7 +4,7 @@ 2007 Michael Droettboom """ -import numpy as N +import numpy as npy from numpy.linalg import inv from sets import Set @@ -37,7 +37,7 @@ class Bbox(TransformNode): def __init__(self, points): TransformNode.__init__(self) - self._points = N.asarray(points, N.float_) + self._points = npy.asarray(points, npy.float_) self.track = False #@staticmethod @@ -52,7 +52,7 @@ #@staticmethod def from_lbrt(*args): - points = N.array(args, dtype=N.float_).reshape(2, 2) + points = npy.array(args, dtype=npy.float_).reshape(2, 2) return Bbox(points) from_lbrt = staticmethod(from_lbrt) @@ -72,12 +72,15 @@ return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ + def __array__(self): + return self._points + # JDH: the update method will update the box limits from the # existing limits and the new data; it appears here you are just # using the new data. We use an "ignore" flag to specify whether # you want to include the existing data or not in the update def update_from_data(self, x, y, ignore=True): - self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_) + self._points = npy.array([[x.min(), y.min()], [x.max(), y.max()]], npy.float_) self.invalidate() # MGDTODO: Probably a more efficient ways to do this... @@ -150,7 +153,7 @@ self.xmax - self.xmin, self.ymax - self.ymin) def _set_bounds(self, bounds): l,b,w,h = bounds - self._points = N.array([[l, b], [l+w, b+h]], N.float_) + self._points = npy.array([[l, b], [l+w, b+h]], npy.float_) self.invalidate() bounds = property(_get_bounds, _set_bounds) @@ -165,7 +168,7 @@ height = self.height deltaw = (sw * width - width) / 2.0 deltah = (sh * height - height) / 2.0 - a = N.array([[-deltaw, -deltah], [deltaw, deltah]]) + a = npy.array([[-deltaw, -deltah], [deltaw, deltah]]) return Bbox(self._points + a) def contains(self, x, y): @@ -215,7 +218,7 @@ raise TypeError("Can not add Transform to object of type '%s'" % type(other)) def transform_point(self, point): - return self.__call__([point])[0] + return self.__call__(npy.asarray([point]))[0] def has_inverse(self): raise NotImplementedError() @@ -229,8 +232,6 @@ def is_affine(self): return False -# MGDTODO: Separate out Affine2DBase / Affine2DConcrete so BlendedAffine and CompositeAffine don't have translate/scale/rotate members - class Affine2DBase(Transform): input_dims = 2 output_dims = 2 @@ -246,7 +247,7 @@ #@staticmethod def _concat(a, b): - return N.dot(b, a) + return npy.dot(b, a) _concat = staticmethod(_concat) def to_values(self): @@ -255,7 +256,7 @@ #@staticmethod def matrix_from_values(a, b, c, d, e, f): - affine = N.zeros((3,3), N.float_) + affine = npy.zeros((3,3), npy.float_) affine[0,] = a, c, e affine[1,] = b, d, f affine[2,2] = 1 @@ -267,7 +268,7 @@ def __call__(self, points): """ - Applies the transformation to a set of 2D points and + Applies the transformation to an array of 2D points and returns the result. points must be a numpy array of shape (N, 2), where N is the @@ -277,9 +278,9 @@ # the points to an array in the first place. If we can use # more arrays upstream, that should help here. mtx = self.get_matrix() - points = N.asarray(points, N.float_) + points = npy.asarray(points, npy.float_) points = points.transpose() - points = N.dot(mtx[0:2, 0:2], points) + points = npy.dot(mtx[0:2, 0:2], points) points = points + mtx[0:2, 2:] return points.transpose() @@ -311,7 +312,7 @@ """ Affine2DBase.__init__(self) if matrix is None: - matrix = N.identity(3) + matrix = npy.identity(3) else: assert matrix.shape == (3, 3) self._mtx = matrix @@ -348,19 +349,19 @@ #@staticmethod def identity(): - return Affine2D(N.identity(3)) + return Affine2D(npy.identity(3)) identity = staticmethod(identity) def rotate(self, theta): - a = N.cos(theta) - b = N.sin(theta) + a = npy.cos(theta) + b = npy.sin(theta) rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0) self._mtx = self._concat(self._mtx, rotate_mtx) self.invalidate() return self def rotate_deg(self, degrees): - return self.rotate(degrees*N.pi/180.) + return self.rotate(degrees*npy.pi/180.) def translate(self, tx, ty): translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty) @@ -420,7 +421,7 @@ # This works because we already know the transforms are # separable, though normally one would want to set b and # c to zero. - self._mtx = N.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) + self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) def is_separable(self): return True @@ -444,7 +445,7 @@ y_points = self._y(points) # This works because we already know the transforms are # separable - return N.hstack((x_points[:, 0:1], y_points[:, 1:2])) + return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): @@ -579,6 +580,7 @@ return interval[0] < val and interval[1] > val if __name__ == '__main__': + import copy from random import random import timeit @@ -588,36 +590,38 @@ assert bbox.xmax == 20 assert bbox.ymax == 25 - assert N.all(bbox.min == [10, 15]) - assert N.all(bbox.max == [20, 25]) - assert N.all(bbox.intervalx == (10, 20)) - assert N.all(bbox.intervaly == (15, 25)) + assert npy.all(bbox.min == [10, 15]) + assert npy.all(bbox.max == [20, 25]) + assert npy.all(bbox.intervalx == (10, 20)) + assert npy.all(bbox.intervaly == (15, 25)) assert bbox.width == 10 assert bbox.height == 10 - assert bbox.get_bounds() == (10, 15, 10, 10) + assert bbox.bounds == (10, 15, 10, 10) + print npy.asarray(bbox) + bbox.intervalx = (11, 21) bbox.intervaly = (16, 26) - assert bbox.get_bounds() == (11, 16, 10, 10) + assert bbox.bounds == (11, 16, 10, 10) bbox.xmin = 12 bbox.ymin = 17 bbox.xmax = 22 bbox.ymax = 27 - assert bbox.get_bounds() == (12, 17, 10, 10) + assert bbox.bounds == (12, 17, 10, 10) bbox = Bbox.from_lbwh(10, 11, 12, 13) - assert bbox.get_bounds() == (10, 11, 12, 13) + assert bbox.bounds == (10, 11, 12, 13) - bbox_copy = bbox.copy() + bbox_copy = copy.copy(bbox) assert bbox == bbox_copy bbox_copy.max = (14, 15) - assert bbox.get_bounds() == (10, 11, 12, 13) - assert bbox_copy.get_bounds() == (10, 11, 4, 4) + assert bbox.bounds == (10, 11, 12, 13) + assert bbox_copy.bounds == (10, 11, 4, 4) bbox1 = Bbox([[10., 15.], [20., 25.]]) bbox2 = Bbox([[30., 35.], [40., 45.]]) @@ -634,7 +638,7 @@ -0.49999999999999994, 0.86602540378443871, 0.0, 0.0) - points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_) + points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_) translated_points = translation(points) assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all() scaled_points = scale(points) @@ -655,7 +659,7 @@ t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points") print "Time to transform 10000 x 10 points as tuples:", t.timeit(10) - points2 = N.asarray(points) + points2 = npy.asarray(points) t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2") print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-13 06:29:14 UTC (rev 3845) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-13 12:44:16 UTC (rev 3846) @@ -356,9 +356,9 @@ left, right = self.convert_xunits((x, x + self.width)) bottom, top = self.convert_yunits((y, y + self.height)) - return ( (left, bottom), (left, top), - (right, top), (right, bottom), - ) + return npy.array([[left, bottom], [left, top], + [right, top], [right, bottom]], + npy.float_) def get_x(self): "Return the left coord of the rectangle" Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-09-13 06:29:14 UTC (rev 3845) +++ branches/transforms/lib/matplotlib/text.py 2007-09-13 12:44:16 UTC (rev 3846) @@ -149,7 +149,7 @@ def _get_xy_display(self): 'get the (possibly unit converted) transformed x,y in display coords' x, y = self.get_position() - return self.get_transform()([[x,y]])[0] + return self.get_transform().transform_point((x,y)) def _get_multialignment(self): if self._multialignment is not None: return self._multialignment This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jo...@us...> - 2007-09-13 06:29:33
|
Revision: 3845 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3845&view=rev Author: jouni Date: 2007-09-12 23:29:14 -0700 (Wed, 12 Sep 2007) Log Message: ----------- More work on dviread and usetex in pdf. It is more usable now, so I am renaming the method from _draw_tex to draw_tex. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py trunk/matplotlib/lib/matplotlib/dviread.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007-09-12 20:37:41 UTC (rev 3844) +++ trunk/matplotlib/CHANGELOG 2007-09-13 06:29:14 UTC (rev 3845) @@ -1,3 +1,6 @@ +2007-09-13 The usetex support in the pdf backend is more usable now, + so I am enabling it. - JKS + 2007-09-12 Fixed a Axes.bar unit bug - JDH 2007-09-10 Made skiprows=1 the default on csv2rec - JDH Modified: trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-12 20:37:41 UTC (rev 3844) +++ trunk/matplotlib/lib/matplotlib/backends/backend_pdf.py 2007-09-13 06:29:14 UTC (rev 3845) @@ -527,7 +527,7 @@ widths.append(afmdata.get_width_from_char_name(ch)) except KeyError: matplotlib.verbose.report( - 'No width for %s in %s' % (ch, fullname), 'debug') + 'No width for %s in %s' % (ch, fullname), 'debug-annoying') widths.append(0) differencesArray = [ Name(ch) for ch in enc ] @@ -561,7 +561,7 @@ except KeyError: matplotlib.verbose.report( 'No name for glyph %d in %s' % (ch, fullname), - 'debug') + 'debug-annoying') need_idx = True @@ -1449,9 +1449,7 @@ # Pop off the global transformation self.file.output(Op.grestore) - def _draw_tex(self, gc, x, y, s, prop, angle): - # Rename to draw_tex to enable - + def draw_tex(self, gc, x, y, s, prop, angle): texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() dvifile = texmanager.make_dvi(s, fontsize) @@ -1494,7 +1492,7 @@ elt[3][-1] += next[3][0] elt[4] += next[4]-next[1] else: - elt[3] += [offset, next[3][0]] + elt[3] += [offset*1000.0/dvifont.size, next[3][0]] elt[4] = next[4] del seq[i+1] continue Modified: trunk/matplotlib/lib/matplotlib/dviread.py =================================================================== --- trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-12 20:37:41 UTC (rev 3844) +++ trunk/matplotlib/lib/matplotlib/dviread.py 2007-09-13 06:29:14 UTC (rev 3845) @@ -84,16 +84,22 @@ e = 0 # zero depth else: # glyph x,y,font,g,w = elt - h = (font.scale * font.tfm.height[g]) >> 20 - e = (font.scale * font.tfm.depth[g]) >> 20 + h = _mul2012(font._scale, font._tfm.height[g]) + e = _mul2012(font._scale, font._tfm.depth[g]) minx = min(minx, x) miny = min(miny, y - h) maxx = max(maxx, x + w) maxy = max(maxy, y + e) maxy_pure = max(maxy_pure, y) + if self.dpi is None: + # special case for ease of debugging: output raw dvi coordinates + return mpl_cbook.Bunch(text=self.text, boxes=self.boxes, + width=maxx-minx, height=maxy_pure-miny, + descent=maxy-maxy_pure) + d = self.dpi / (72.27 * 2**16) # from TeX's "scaled points" to dpi units - text = [ ((x-minx)*d, (maxy-y)*d, DviFont(f), g, w*d) + text = [ ((x-minx)*d, (maxy-y)*d, f, g, w*d) for (x,y,f,g,w) in self.text ] boxes = [ ((x-minx)*d, (maxy-y)*d, h*d, w*d) for (x,y,h,w) in self.boxes ] @@ -110,11 +116,11 @@ while True: byte = ord(self.file.read(1)) self._dispatch(byte) - if self.state == _dvistate.inpage: - matplotlib.verbose.report( - 'Dvi._read: after %d at %f,%f' % - (byte, self.h, self.v), - 'debug-annoying') +# if self.state == _dvistate.inpage: +# matplotlib.verbose.report( +# 'Dvi._read: after %d at %f,%f' % +# (byte, self.h, self.v), +# 'debug-annoying') if byte == 140: # end of page return True if self.state == _dvistate.post_post: # end of file @@ -225,21 +231,11 @@ # I think we can assume this is constant self.state = _dvistate.outer - def _width_of(self, char, font): - width = font.tfm.width.get(char, None) - if width is not None: - return (width * font.scale) >> 20 - - matplotlib.verbose.report( - 'No width for char %d in font %s' % (char, font.name), - 'debug') - return 0 - def _set_char(self, char): if self.state != _dvistate.inpage: raise ValueError, "misplaced set_char in dvi file" self._put_char(char) - self.h += self._width_of(char, self.fonts[self.f]) + self.h += self.fonts[self.f]._width_of(char) def _set_rule(self, a, b): if self.state != _dvistate.inpage: @@ -251,20 +247,33 @@ if self.state != _dvistate.inpage: raise ValueError, "misplaced put_char in dvi file" font = self.fonts[self.f] - if font.vf is None: + if font._vf is None: self.text.append((self.h, self.v, font, char, - self._width_of(char, font))) + font._width_of(char))) +# matplotlib.verbose.report( +# 'Dvi._put_char: %d,%d %d' %(self.h, self.v, char), +# 'debug-annoying') else: - self.text.extend([(self.h + x, self.v + y, f, g, w) - for x, y, f, g, w in font.vf[char].text]) - self.boxes.extend([(self.h + x, self.v + y, a, b) - for x, y, a, b in font.vf[char].boxes]) + scale = font._scale + for x, y, f, g, w in font._vf[char].text: + newf = DviFont(scale=_mul2012(scale, f._scale), + tfm=f._tfm, texname=f.texname, vf=f._vf) + self.text.append((self.h + _mul2012(x, scale), + self.v + _mul2012(y, scale), + newf, g, newf._width_of(g))) + self.boxes.extend([(self.h + _mul2012(x, scale), + self.v + _mul2012(y, scale), + _mul2012(a, scale), _mul2012(b, scale)) + for x, y, a, b in font._vf[char].boxes]) def _put_rule(self, a, b): if self.state != _dvistate.inpage: raise ValueError, "misplaced put_rule in dvi file" if a > 0 and b > 0: self.boxes.append((self.h, self.v, a, b)) +# matplotlib.verbose.report( +# 'Dvi._put_rule: %d,%d %d,%d' % (self.h, self.v, a, b), +# 'debug-annoying') def _nop(self): pass @@ -357,7 +366,7 @@ vf = _vffile(n[-l:]) - self.fonts[k] = mpl_cbook.Bunch(scale=s, tfm=tfm, name=n, vf=vf) + self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf) def _post(self): if self.state != _dvistate.outer: @@ -370,17 +379,20 @@ raise NotImplementedError class DviFont(object): - __slots__ = ('texname', 'size') + """ + Object that holds a font's texname and size and supports comparison. + There are also internal attributes (for use by dviread.py) that + are _not_ used for comparison. - def __init__(self, f): - """ - Object that holds a font's texname and size and supports comparison. + The size is in Adobe points (converted from TeX points). + """ + __slots__ = ('texname', 'size', '_scale', '_vf', '_tfm') - The size is in Adobe points (converted from TeX points). - """ + def __init__(self, scale, tfm, texname, vf): + self._scale, self._tfm, self.texname, self._vf = \ + scale, tfm, texname, vf # TODO: would it make more sense to have the size in dpi units? - self.texname = f.name - self.size = f.scale * (72.0 / (72.27 * 2**16)) + self.size = scale * (72.0 / (72.27 * 2**16)) def __eq__(self, other): return self.__class__ == other.__class__ and \ @@ -389,6 +401,16 @@ def __ne__(self, other): return not self.__eq__(other) + def _width_of(self, char): + width = self._tfm.width.get(char, None) + if width is not None: + return _mul2012(width, self._scale) + + matplotlib.verbose.report( + 'No width for char %d in font %s' % (char, self.texname), + 'debug') + return 0 + class Vf(Dvi): """ A virtual font (*.vf file) containing subroutines for dvi files. @@ -465,7 +487,8 @@ raise ValueError, "pre command in middle of vf file" if i != 202: raise ValueError, "Unknown vf format %d" % i - matplotlib.verbose.report('vf file comment: ' + x, 'debug') + if len(x): + matplotlib.verbose.report('vf file comment: ' + x, 'debug') self.state = _dvistate.outer # cs = checksum, ds = design size @@ -474,7 +497,7 @@ if self._first_font is None: self._first_font = k -def fix2comp(num): +def _fix2comp(num): """ Convert from two's complement to negative. """ @@ -484,6 +507,13 @@ else: return num +def _mul2012(num1, num2): + """ + Multiply two numbers in 20.12 fixed point format. + """ + # Separated into a function because >> has surprising precedence + return (num1*num2) >> 20 + class Tfm(object): """ A TeX Font Metric file. This implementation covers only the bare @@ -497,6 +527,7 @@ (this is a dict because indexing may not start from 0) height[i], depth[i]: height and depth of character #i """ + __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth') def __init__(self, filename): matplotlib.verbose.report('opening tfm file ' + filename, 'debug') @@ -525,9 +556,9 @@ [ struct.unpack('!%dI' % (len(x)/4), x) for x in (widths, heights, depths) ] for i in range(ec-bc): - self.width[bc+i] = fix2comp(widths[ord(char_info[4*i])]) - self.height[bc+i] = fix2comp(heights[ord(char_info[4*i+1]) >> 4]) - self.depth[bc+i] = fix2comp(depths[ord(char_info[4*i+1]) & 0xf]) + self.width[bc+i] = _fix2comp(widths[ord(char_info[4*i])]) + self.height[bc+i] = _fix2comp(heights[ord(char_info[4*i+1]) >> 4]) + self.depth[bc+i] = _fix2comp(depths[ord(char_info[4*i+1]) & 0xf]) class PsfontsMap(object): @@ -552,6 +583,7 @@ the pdf-related files perhaps only avoid the "Base 14" pdf fonts. But the user may have configured these files differently. """ + __slots__ = ('_font',) def __init__(self, filename): self._font = {} @@ -627,7 +659,17 @@ encoding=encoding, filename=filename) class Encoding(object): + """ + Parses a *.enc file referenced from a psfonts.map style file. + The format this class understands is a very limited subset of + PostScript. + Usage (subject to change): + for name in Encoding(filename): + whatever(name) + """ + __slots__ = ('encoding',) + def __init__(self, filename): file = open(filename, 'rt') try: @@ -694,6 +736,10 @@ return result +# With multiple text objects per figure (e.g. tick labels) we may end +# up reading the same tfm and vf files many times, so we implement a +# simple cache. TODO: is this worth making persistent? + _tfmcache = {} _vfcache = {} @@ -721,19 +767,22 @@ if __name__ == '__main__': - matplotlib.verbose.set_level('debug') - dvi = Dvi('foo.dvi', 72) + import sys + matplotlib.verbose.set_level('debug-annoying') + fname = sys.argv[1] + try: dpi = float(sys.argv[2]) + except IndexError: dpi = None + dvi = Dvi(fname, dpi) fontmap = PsfontsMap(find_tex_file('pdftex.map')) - for text,boxes in dvi: + for page in dvi: print '=== new page ===' fPrev = None - for x,y,f,c in text: - texname = dvi.fonts[f].name - print x,y,c,chr(c),texname + for x,y,f,c,w in page.text: if f != fPrev: - print 'font', texname, '=', fontmap[texname].__dict__ + print 'font', f.texname, 'scaled', f._scale/pow(2.0,20) fPrev = f - for x,y,w,h in boxes: + print x,y,c, 32 <= c < 128 and chr(c) or '.', w + for x,y,w,h in page.boxes: print x,y,'BOX',w,h This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-09-12 20:37:47
|
Revision: 3844 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3844&view=rev Author: jdh2358 Date: 2007-09-12 13:37:41 -0700 (Wed, 12 Sep 2007) Log Message: ----------- fixed a bar units bug Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/artist.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/axis.py trunk/matplotlib/lib/matplotlib/mlab.py trunk/matplotlib/lib/matplotlib/patches.py Added Paths: ----------- trunk/matplotlib/examples/units/bar_demo2.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/API_CHANGES 2007-09-12 20:37:41 UTC (rev 3844) @@ -1,3 +1,5 @@ + Made skiprows=1 the default on csv2rec + The gd and paint backends have been deleted. The errorbar method and function now accept additional kwargs Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/CHANGELOG 2007-09-12 20:37:41 UTC (rev 3844) @@ -1,3 +1,7 @@ +2007-09-12 Fixed a Axes.bar unit bug - JDH + +2007-09-10 Made skiprows=1 the default on csv2rec - JDH + 2007-09-09 Split out the plotting part of pylab and put it in pyplot.py; removed numerix from the remaining pylab.py, which imports everything from pyplot.py. The intention Added: trunk/matplotlib/examples/units/bar_demo2.py =================================================================== --- trunk/matplotlib/examples/units/bar_demo2.py (rev 0) +++ trunk/matplotlib/examples/units/bar_demo2.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -0,0 +1,34 @@ +""" +plot using a variety of cm vs inches conversions. The example shows +how default unit instrospection works (ax1), how various keywords can +be used to set the x and y units to override the defaults (ax2, ax3, +ax4) and how one can set the xlimits using scalars (ax3, current units +assumed) or units (conversions applied to get the numbers to current +units) + +""" +from basic_units import cm, inch +from pylab import figure, show, nx + +cms = cm *nx.arange(0, 10, 2) +bottom=0*cm +width=0.8*cm + +fig = figure() + +ax1 = fig.add_subplot(2,2,1) +ax1.bar(cms, cms, bottom=bottom) + +ax2 = fig.add_subplot(2,2,2) +ax2.bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch) + +ax3 = fig.add_subplot(2,2,3) +ax3.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm) +ax3.set_xlim(3, 6) # scalars are interpreted in current units + +ax4 = fig.add_subplot(2,2,4) +ax4.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch) +#fig.savefig('simple_conversion_plot.png') +ax4.set_xlim(3*cm, 6*cm) # cm are converted to inches + +show() Modified: trunk/matplotlib/lib/matplotlib/artist.py =================================================================== --- trunk/matplotlib/lib/matplotlib/artist.py 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/lib/matplotlib/artist.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -51,7 +51,7 @@ self._remove_method = None def remove(self): - ''' + """ Remove the artist from the figure if possible. The effect will not be visible until the figure is redrawn, e.g., with ax.draw_idle(). Call ax.relim() to update the axes limits if desired. @@ -60,7 +60,7 @@ was added to axes with autolim=True. Note: there is no support for removing the artist's legend entry. - ''' + """ # There is no method to set the callback. Instead the parent should set # the _remove_method attribute directly. This would be a protected Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -1198,23 +1198,27 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None): 'look for unit kwargs and update the axis instances as necessary' - if self.xaxis is None or self.xaxis is None: return + if self.xaxis is None or self.yaxis is None: return - + #print 'processing', self.get_geometry() if xdata is not None: self.xaxis.update_units(xdata) + #print '\tset from xdata', self.xaxis.units if ydata is not None: self.yaxis.update_units(ydata) + #print '\tset from ydata', self.yaxis.units # process kwargs 2nd since these will override default units if kwargs is not None: xunits = kwargs.pop( 'xunits', self.xaxis.units) if xunits!=self.xaxis.units: + #print '\tkw setting xunits', xunits self.xaxis.set_units(xunits) yunits = kwargs.pop('yunits', self.yaxis.units) if yunits!=self.yaxis.units: + #print '\tkw setting yunits', yunits self.yaxis.set_units(yunits) def in_axes(self, xwin, ywin): @@ -3114,11 +3118,13 @@ else: raise ValueError, 'invalid orientation: %s' % orientation - left = npy.asarray(left) - height = npy.asarray(height) - width = npy.asarray(width) - bottom = npy.asarray(bottom) + # do not convert to array here as unit info is lost + #left = npy.asarray(left) + #height = npy.asarray(height) + #width = npy.asarray(width) + #bottom = npy.asarray(bottom) + if len(linewidth) == 1: linewidth = linewidth * nbars # if color looks like a color string, an RGB tuple or a @@ -3161,14 +3167,14 @@ # lets do some conversions now if self.xaxis is not None: xconv = self.xaxis.converter - if ( xconv ): + if xconv is not None: units = self.xaxis.get_units() left = xconv.convert( left, units ) width = xconv.convert( width, units ) if self.yaxis is not None: yconv = self.yaxis.converter - if ( yconv ): + if yconv is not None : units = self.yaxis.get_units() bottom = yconv.convert( bottom, units ) height = yconv.convert( height, units ) @@ -3208,12 +3214,14 @@ if xerr is not None or yerr is not None: if orientation == 'vertical': - x = left + 0.5*width - y = bottom + height + # using list comps rather than arrays to preserve unit info + x = [l+0.5*w for l, w in zip(left, width)] + y = [b+h for b,h in zip(bottom, height)] elif orientation == 'horizontal': - x = left + width - y = bottom + 0.5*height + # using list comps rather than arrays to preserve unit info + x = [l+w for l,w in zip(left, width)] + y = [b+0.5*h for b,h in zip(bottom, height)] self.errorbar( x, y, Modified: trunk/matplotlib/lib/matplotlib/axis.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axis.py 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/lib/matplotlib/axis.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -828,7 +828,7 @@ return x ret = self.converter.convert(x, self.units) - #print 'convert_units converting: units=%s, converter=%s, in=%s, out=%s'%(self.units, self.converter, x, ret) + #print 'convert_units converting: axis=%s, units=%s, converter=%s, in=%s, out=%s'%(self, self.units, self.converter, x, ret) return ret def set_units(self, u): Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -1253,9 +1253,9 @@ if r==1 or c==1: X.shape = max(r,c), if unpack: return X.transpose() - return X + else: return X -def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',', +def csv2rec(fname, comments='#', skiprows=1, checkrows=5, delimiter=',', converterd=None, names=None, missing=None): """ Load data from comma/space/tab delimited file in fname into a Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2007-09-12 20:25:17 UTC (rev 3843) +++ trunk/matplotlib/lib/matplotlib/patches.py 2007-09-12 20:37:41 UTC (rev 3844) @@ -77,6 +77,8 @@ if len(kwargs): artist.setp(self, **kwargs) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd + + def contains(self, mouseevent): """Test whether the mouse event occurred in the patch. @@ -347,7 +349,6 @@ Return the vertices of the rectangle """ x, y = self.xy - left, right = self.convert_xunits((x, x + self.width)) bottom, top = self.convert_yunits((y, y + self.height)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-09-12 20:25:19
|
Revision: 3843 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3843&view=rev Author: jdh2358 Date: 2007-09-12 13:25:17 -0700 (Wed, 12 Sep 2007) Log Message: ----------- minor changes for gtk navigation Modified Paths: -------------- branches/transforms/lib/matplotlib/backends/backend_gtk.py branches/transforms/lib/matplotlib/backends/backend_gtkagg.py Modified: branches/transforms/lib/matplotlib/backends/backend_gtk.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtk.py 2007-09-12 19:47:56 UTC (rev 3842) +++ branches/transforms/lib/matplotlib/backends/backend_gtk.py 2007-09-12 20:25:17 UTC (rev 3843) @@ -405,8 +405,8 @@ self.toolbar = self._get_toolbar(canvas) # calculate size for window - w = int (self.canvas.figure.bbox.width()) - h = int (self.canvas.figure.bbox.height()) + w = int (self.canvas.figure.bbox.width) + h = int (self.canvas.figure.bbox.height) if self.toolbar is not None: self.toolbar.show() @@ -518,7 +518,7 @@ gc = drawable.new_gc() - height = self.canvas.figure.bbox.height() + height = self.canvas.figure.bbox.height y1 = height - y1 y0 = height - y0 @@ -639,8 +639,8 @@ toolfig.subplots_adjust(top=0.9) tool = SubplotTool(self.canvas.figure, toolfig) - w = int (toolfig.bbox.width()) - h = int (toolfig.bbox.height()) + w = int (toolfig.bbox.width) + h = int (toolfig.bbox.height) window = gtk.Window() Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-12 19:47:56 UTC (rev 3842) +++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-12 20:25:17 UTC (rev 3843) @@ -60,8 +60,9 @@ w,h = widget.window.get_size() if w==1 or h==1: return # empty fig + # dpival = self.figure.dpi.get() MGDTODO # compute desired figure size in inches - dpival = self.figure.dpi.get() + dpival = self.figure.dpi winch = w/dpival hinch = h/dpival self.figure.set_size_inches(winch, hinch) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-12 19:48:00
|
Revision: 3842 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3842&view=rev Author: mdboom Date: 2007-09-12 12:47:56 -0700 (Wed, 12 Sep 2007) Log Message: ----------- More progress. Zooming and panning working thanks to John's patch. Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py branches/transforms/lib/matplotlib/artist.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/backends/backend_tkagg.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -8,9 +8,16 @@ from numpy.linalg import inv from sets import Set +# MGDTODO: The name of this module is bad, since it deals with +# non-affine transformations as well. It should probably just be +# "transforms", but we already had one of those... ;) + # MGDTODO: This creates a ton of cyclical references. We may want to # consider using weak references +# MGDTODO: deep copying is probably incorrect wrt the parent/child +# relationships + class TransformNode(object): def __init__(self): self._parents = Set() @@ -48,29 +55,31 @@ points = N.array(args, dtype=N.float_).reshape(2, 2) return Bbox(points) from_lbrt = staticmethod(from_lbrt) + + def __copy__(self): + return Bbox(self._points.copy()) + def __deepcopy__(self, memo): + return Bbox(self._points.copy()) + def __cmp__(self, other): # MGDTODO: Totally suboptimal - if isinstance(other, Bbox): - if (self._points == other._points).all(): - return 0 + if isinstance(other, Bbox) and (self._points == other._points).all(): + return 0 return -1 - + + def __repr__(self): + return 'Bbox(%s)' % repr(self._points) + __str__ = __repr__ + # JDH: the update method will update the box limits from the # existing limits and the new data; it appears here you are just # using the new data. We use an "ignore" flag to specify whether # you want to include the existing data or not in the update - def update_from_data(self, x, y): + def update_from_data(self, x, y, ignore=True): self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_) self.invalidate() - - def copy(self): - return Bbox(self._points.copy()) - def __repr__(self): - return 'Bbox(%s)' % repr(self._points) - __str__ = __repr__ - # MGDTODO: Probably a more efficient ways to do this... def _get_xmin(self): return self._points[0, 0] @@ -136,19 +145,24 @@ return self.ymax - self.ymin height = property(_get_height) + def _get_bounds(self): + return (self.xmin, self.ymin, + self.xmax - self.xmin, self.ymax - self.ymin) + def _set_bounds(self, bounds): + l,b,w,h = bounds + self._points = N.array([[l, b], [l+w, b+h]], N.float_) + self.invalidate() + bounds = property(_get_bounds, _set_bounds) + def transformed(self, transform): return Bbox(transform(self._points)) def inverse_transformed(self, transform): return Bbox(transform.inverted()(self._points)) - def get_bounds(self): - return (self.xmin, self.ymin, - self.xmax - self.xmin, self.ymax - self.ymin) - def expanded(self, sw, sh): - width = self.width() - height = self.height() + width = self.width + height = self.height deltaw = (sw * width - width) / 2.0 deltah = (sh * height - height) / 2.0 a = N.array([[-deltaw, -deltah], [deltaw, deltah]]) @@ -199,6 +213,9 @@ if isinstance(other, Transform): return composite_transform_factory(other, self) raise TypeError("Can not add Transform to object of type '%s'" % type(other)) + + def transform_point(self, point): + return self.__call__([point])[0] def has_inverse(self): raise NotImplementedError() @@ -211,49 +228,27 @@ def is_affine(self): return False - -class Affine2D(Transform): + +# MGDTODO: Separate out Affine2DBase / Affine2DConcrete so BlendedAffine and CompositeAffine don't have translate/scale/rotate members + +class Affine2DBase(Transform): input_dims = 2 output_dims = 2 - - def __init__(self, matrix = None): - """ - Initialize an Affine transform from a 3x3 numpy float array. - a c e - b d f - 0 0 1 - """ + def __init__(self): Transform.__init__(self) - if matrix is None: - matrix = N.identity(3) - else: - assert matrix.shape == (3, 3) - self._mtx = matrix self._inverted = None - def __repr__(self): - return "Affine2D(%s)" % repr(self._mtx) - __str__ = __repr__ - - def __cmp__(self, other): - # MGDTODO: We need to decide if we want deferred transforms - # to be equal to this one - if isinstance(other, Affine2D): - if (self.get_matrix() == other.get_matrix()).all(): - return 0 - return -1 - def _do_invalidation(self): result = self._inverted is None self._inverted = None return result + + #@staticmethod + def _concat(a, b): + return N.dot(b, a) + _concat = staticmethod(_concat) - #@staticmethod - def from_values(a, b, c, d, e, f): - return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f)) - from_values = staticmethod(from_values) - def to_values(self): mtx = self.get_matrix() return tuple(mtx[:2].swapaxes(0, 1).flatten()) @@ -268,7 +263,7 @@ matrix_from_values = staticmethod(matrix_from_values) def get_matrix(self): - return self._mtx + raise NotImplementedError() def __call__(self, points): """ @@ -278,26 +273,74 @@ points must be a numpy array of shape (N, 2), where N is the number of points. """ - # MGDTODO: This involves a copy. We may need to do something like - # http://neuroimaging.scipy.org/svn/ni/ni/trunk/neuroimaging/core/reference/mapping.py - # to separate the matrix out into the translation and scale components - # and apply each separately (which is still sub-optimal) - - # This is easier for now, however, since we can just keep a - # regular affine matrix around - # MGDTODO: Trap cases where this isn't an array and fix there + # MGDTODO: The major speed trap here is just converting to + # the points to an array in the first place. If we can use + # more arrays upstream, that should help here. mtx = self.get_matrix() points = N.asarray(points, N.float_) - new_points = points.swapaxes(0, 1) - new_points = N.vstack((new_points, N.ones((1, points.shape[0])))) - result = N.dot(mtx, new_points)[:2] - return result.swapaxes(0, 1) + points = points.transpose() + points = N.dot(mtx[0:2, 0:2], points) + points = points + mtx[0:2, 2:] + return points.transpose() + def inverted(self): + if self._inverted is None: + mtx = self.get_matrix() + self._inverted = Affine2D(inv(mtx)) + return self._inverted + + def is_separable(self): + mtx = self.get_matrix() + return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 + + def is_affine(self): + return True + + +class Affine2D(Affine2DBase): + input_dims = 2 + output_dims = 2 + + def __init__(self, matrix = None): + """ + Initialize an Affine transform from a 3x3 numpy float array. + + a c e + b d f + 0 0 1 + """ + Affine2DBase.__init__(self) + if matrix is None: + matrix = N.identity(3) + else: + assert matrix.shape == (3, 3) + self._mtx = matrix + self._inverted = None + + def __repr__(self): + return "Affine2D(%s)" % repr(self._mtx) + __str__ = __repr__ + + def __cmp__(self, other): + if (isinstance(other, Affine2D) and + (self.get_matrix() == other.get_matrix()).all()): + return 0 + return -1 + + def __copy__(self): + return Affine2D(self._mtx.copy()) + + def __deepcopy__(self, memo): + return Affine2D(self._mtx.copy()) + #@staticmethod - def _concat(a, b): - return N.dot(b, a) - _concat = staticmethod(_concat) + def from_values(a, b, c, d, e, f): + return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f)) + from_values = staticmethod(from_values) + def get_matrix(self): + return self._mtx + #@staticmethod def concat(a, b): return Affine2D(Affine2D._concat(a._mtx, b._mtx)) @@ -346,19 +389,18 @@ def is_affine(self): return True -class BlendedAffine2D(Affine2D): +class BlendedAffine2D(Affine2DBase): def __init__(self, x_transform, y_transform): assert x_transform.is_affine() assert y_transform.is_affine() assert x_transform.is_separable() assert y_transform.is_separable() - Transform.__init__(self) + Affine2DBase.__init__(self) self.add_children([x_transform, y_transform]) self._x = x_transform self._y = y_transform self._mtx = None - self._inverted = None def __repr__(self): return "BlendedAffine2D(%s,%s)" % (self._x, self._y) @@ -367,7 +409,7 @@ def _do_invalidation(self): if self._mtx is not None: self._mtx = None - Affine2D._do_invalidation(self) + Affine2DBase._do_invalidation(self) return False return True @@ -376,8 +418,9 @@ x_mtx = self._x.get_matrix() y_mtx = self._y.get_matrix() # This works because we already know the transforms are - # separable - self._mtx = N.vstack([x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]]) + # separable, though normally one would want to set b and + # c to zero. + self._mtx = N.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) def is_separable(self): return True @@ -397,20 +440,22 @@ self._y = y_transform def __call__(self, points): - # MGDTODO: Implement me - pass + x_points = self._x(points) + y_points = self._y(points) + # This works because we already know the transforms are + # separable + return N.hstack((x_points[:, 0:1], y_points[:, 1:2])) -class CompositeAffine2D(Affine2D): +class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): assert a.is_affine() assert b.is_affine() - Transform.__init__(self) + Affine2DBase.__init__(self) self.add_children([a, b]) self._a = a self._b = b self._mtx = None - self._inverted = None def __repr__(self): return "CompositeAffine2D(%s, %s)" % (self._a, self._b) @@ -418,7 +463,7 @@ def _do_invalidation(self): self._mtx = None - Affine2D._do_invalidation(self) + Affine2DBase._do_invalidation(self) def _make__mtx(self): if self._mtx is None: @@ -433,22 +478,23 @@ class CompositeTransform(Transform): def __init__(self, a, b): assert a.output_dims == b.input_dims - + self.input_dims = a.input_dims + self.output_dims = b.output_dims + Transform.__init__(self) self.add_children([a, b]) self._a = a self._b = b def __call__(self, points): - # MGDTODO: Optimize here by concatenating affines if possible return self._b(self._a(points)) -class BboxTransform(Affine2D): +class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): assert isinstance(boxin, Bbox) assert isinstance(boxout, Bbox) - Transform.__init__(self) + Affine2DBase.__init__(self) self.add_children([boxin, boxout]) self._boxin = boxin self._boxout = boxout @@ -462,7 +508,7 @@ def _do_invalidation(self): if self._mtx is not None: self._mtx = None - Affine2D._do_invalidation(self) + Affine2DBase._do_invalidation(self) return False return True @@ -481,14 +527,6 @@ self._mtx = affine._mtx - def __call__(self, points): - self._make__mtx() - return Affine2D.__call__(self, points) - - def inverted(self): - self._make__mtx() - return Affine2D.inverted(self) - def is_separable(self): return True @@ -541,6 +579,9 @@ return interval[0] < val and interval[1] > val if __name__ == '__main__': + from random import random + import timeit + bbox = Bbox.from_lbrt(10., 15., 20., 25.) assert bbox.xmin == 10 assert bbox.ymin == 15 @@ -589,7 +630,9 @@ scale = Affine2D().scale(10, 20) assert scale.to_values() == (10, 0, 0, 20, 0, 0) rotation = Affine2D().rotate_deg(30) - print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, -0.49999999999999994, 0.86602540378443871, 0.0, 0.0) + print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, + -0.49999999999999994, 0.86602540378443871, + 0.0, 0.0) points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_) translated_points = translation(points) @@ -600,11 +643,20 @@ print rotated_points tpoints1 = rotation(translation(scale(points))) - trans_sum = rotation + translation + scale + trans_sum = scale + translation + rotation tpoints2 = trans_sum(points) print tpoints1, tpoints2 print tpoints1 == tpoints2 # Need to do some sort of fuzzy comparison here? # assert (tpoints1 == tpoints2).all() + + # Here are some timing tests + points = [(random(), random()) for i in xrange(10000)] + t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points") + print "Time to transform 10000 x 10 points as tuples:", t.timeit(10) + + points2 = N.asarray(points) + t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, points2") + print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10) __all__ = ['Transform', 'Affine2D'] Modified: branches/transforms/lib/matplotlib/artist.py =================================================================== --- branches/transforms/lib/matplotlib/artist.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/artist.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -337,7 +337,7 @@ def _set_gc_clip(self, gc): 'set the clip properly for the gc' if self.clipbox is not None: - gc.set_clip_rectangle(self.clipbox.get_bounds()) + gc.set_clip_rectangle(self.clipbox.bounds) gc.set_clip_path(self._clippath) def draw(self, renderer, *args, **kwargs): Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -1,5 +1,5 @@ from __future__ import division, generators -import math, sys, warnings +import math, sys, warnings, copy import numpy as npy @@ -483,7 +483,7 @@ """ martist.Artist.__init__(self) self._position = maffine.Bbox.from_lbwh(*rect) - self._originalPosition = self._position.copy() + self._originalPosition = copy.deepcopy(self._position) self.set_axes(self) self.set_aspect('auto') self.set_adjustable('box') @@ -613,7 +613,7 @@ """ martist.Artist.set_figure(self, fig) - l, b, w, h = self._position.get_bounds() + l, b, w, h = self._position.bounds xmin = fig.bbox.xmin xmax = fig.bbox.xmax ymin = fig.bbox.ymin @@ -669,9 +669,9 @@ def get_position(self, original=False): 'Return the axes rectangle left, bottom, width, height' if original: - return self._originalPosition[:] + return self._originalPosition.bounds else: - return self._position[:] + return self._position.bounds # return [val.get() for val in self._position] def set_position(self, pos, which='both'): @@ -694,10 +694,10 @@ # # Change values within self._position--don't replace it. # for num,val in zip(pos, self._position): # val.set(num) - self._position = pos + self._position.bounds = pos.bounds # MGDTODO: side-effects if which in ('both', 'original'): - self._originalPosition = pos + self._originalPosition.bounds = pos.bounds def _set_artist_props(self, a): @@ -1547,7 +1547,9 @@ def get_xscale(self): 'return the xaxis scale string: log or linear' - return self.scaled[self.transData.get_funcx().get_type()] + # MGDTODO + # return self.scaled[self.transData.get_funcx().get_type()] + return 'linear' def set_xscale(self, value, basex = 10, subsx=None): """ @@ -1671,7 +1673,8 @@ def get_yscale(self): 'return the yaxis scale string: log or linear' - return self.scaled[self.transData.get_funcy().get_type()] + # return self.scaled[self.transData.get_funcy().get_type()] + return 'linear' def set_yscale(self, value, basey=10, subsy=None): """ Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -4,7 +4,7 @@ """ from __future__ import division -import os, sys, warnings +import os, sys, warnings, copy import numpy as npy import matplotlib.numerix.npyma as ma @@ -1070,7 +1070,7 @@ def get_width_height(self): """return the figure width and height in points or pixels (depending on the backend), truncated to integers""" - return int(self.figure.bbox.width()), int(self.figure.bbox.height()) + return int(self.figure.bbox.width), int(self.figure.bbox.height) filetypes = { 'emf': 'Enhanced Metafile', @@ -1544,7 +1544,7 @@ xmin, xmax = a.get_xlim() ymin, ymax = a.get_ylim() lim = xmin, xmax, ymin, ymax - self._xypress.append((x, y, a, i, lim,a.transData.deepcopy())) + self._xypress.append((x, y, a, i, lim, copy.deepcopy(a.transData))) self.canvas.mpl_disconnect(self._idDrag) self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan) @@ -1571,7 +1571,7 @@ xmin, xmax = a.get_xlim() ymin, ymax = a.get_ylim() lim = xmin, xmax, ymin, ymax - self._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() )) + self._xypress.append(( x, y, a, i, lim, copy.deepcopy(a.transData) )) self.press(event) @@ -1637,8 +1637,9 @@ #safer to use the recorded button at the press than current button: #multiple button can get pressed during motion... if self._button_pressed==1: - lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) ) - x, y = trans.inverse_xy_tup( (event.x, event.y) ) + inverse = trans.inverted() + lastx, lasty = inverse.transform_point((lastx, lasty)) + x, y = inverse.transform_point( (event.x, event.y) ) if a.get_xscale()=='log': dx=1-lastx/x else: @@ -1664,15 +1665,16 @@ ymax -= dy elif self._button_pressed==3: try: - dx=(lastx-event.x)/float(a.bbox.width()) - dy=(lasty-event.y)/float(a.bbox.height()) + dx=(lastx-event.x)/float(a.bbox.width) + dy=(lasty-event.y)/float(a.bbox.height) dx,dy=format_deltas(event,dx,dy) if a.get_aspect() != 'auto': dx = 0.5*(dx + dy) dy = dx alphax = pow(10.0,dx) alphay = pow(10.0,dy)#use logscaling, avoid singularities and smother scaling... - lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) ) + inverse = trans.inverted() + lastx, lasty = inverse.transform_point( (lastx, lasty) ) if a.get_xscale()=='log': xmin = lastx*(xmin/lastx)**alphax xmax = lastx*(xmax/lastx)**alphax @@ -1710,8 +1712,9 @@ xmin, ymin, xmax, ymax = lim # zoom to rect - lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) ) - x, y = a.transData.inverse_xy_tup( (x, y) ) + inverse = a.transData.inverted() + lastx, lasty = inverse.transform_point( (lastx, lasty) ) + x, y = inverse.transform_point( (x, y) ) Xmin,Xmax=a.get_xlim() Ymin,Ymax=a.get_ylim() Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -398,7 +398,7 @@ self.figure.draw(self.renderer) def get_renderer(self): - l,b,w,h = self.figure.bbox.get_bounds() + l,b,w,h = self.figure.bbox.bounds # MGDTODO # key = w, h, self.figure.dpi.get() key = w, h, self.figure.dpi Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-12 18:22:24 UTC (rev 3841) +++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-12 19:47:56 UTC (rev 3842) @@ -147,7 +147,7 @@ def __init__(self, figure, master=None, resize_callback=None): FigureCanvasAgg.__init__(self, figure) self._idle = True - t1,t2,w,h = self.figure.bbox.get_bounds() + t1,t2,w,h = self.figure.bbox.bounds w, h = int(w), int(h) self._tkcanvas = Tk.Canvas( master=master, width=w, height=h, borderwidth=4) @@ -288,7 +288,7 @@ self.window.wm_title("Figure %d" % num) self.canvas = canvas self._num = num - t1,t2,w,h = canvas.figure.bbox.get_bounds() + t1,t2,w,h = canvas.figure.bbox.bounds w, h = int(w), int(h) self.window.minsize(int(w*3/4),int(h*3/4)) if matplotlib.rcParams['toolbar']=='classic': @@ -436,7 +436,7 @@ self.canvas = canvas self.window = window - xmin, xmax = canvas.figure.bbox.intervalx().get_bounds() + xmin, xmax = canvas.figure.bbox.intervalx height, width = 50, xmax-xmin Tk.Frame.__init__(self, master=self.window, width=width, height=height, @@ -582,7 +582,7 @@ self.message.set(s) def draw_rubberband(self, event, x0, y0, x1, y1): - height = self.canvas.figure.bbox.height() + height = self.canvas.figure.bbox.height y0 = height-y0 y1 = height-y1 try: self.lastrect This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2007-09-12 18:22:26
|
Revision: 3841 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3841&view=rev Author: jswhit Date: 2007-09-12 11:22:24 -0700 (Wed, 12 Sep 2007) Log Message: ----------- regenerate with Cython 0.9.6.6 Modified Paths: -------------- trunk/toolkits/basemap/src/_geod.c trunk/toolkits/basemap/src/_proj.c Modified: trunk/toolkits/basemap/src/_geod.c =================================================================== --- trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:14:38 UTC (rev 3840) +++ trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:22:24 UTC (rev 3841) @@ -1,4 +1,4 @@ -/* Generated by Pyrex 0.9.6.5 on Wed Sep 12 12:12:33 2007 */ +/* Generated by Cython 0.9.6.6 on Wed Sep 12 12:20:59 2007 */ #define PY_SSIZE_T_CLEAN #include "Python.h" @@ -50,8 +50,14 @@ #ifdef __GNUC__ +/* Test for GCC > 2.95 */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) +#else /* __GNUC__ > 2 ... */ +#define likely(x) (x) +#define unlikely(x) (x) +#endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) Modified: trunk/toolkits/basemap/src/_proj.c =================================================================== --- trunk/toolkits/basemap/src/_proj.c 2007-09-12 18:14:38 UTC (rev 3840) +++ trunk/toolkits/basemap/src/_proj.c 2007-09-12 18:22:24 UTC (rev 3841) @@ -1,4 +1,4 @@ -/* Generated by Pyrex 0.9.6.3 on Fri Aug 31 08:42:47 2007 */ +/* Generated by Cython 0.9.6.6 on Wed Sep 12 12:21:02 2007 */ #define PY_SSIZE_T_CLEAN #include "Python.h" @@ -12,6 +12,8 @@ #define PY_SSIZE_T_MIN INT_MIN #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) PyInt_AsLong(o) + #define PyNumber_Index(o) PyNumber_Int(o) + #define PyIndex_Check(o) PyNumber_Check(o) #endif #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" @@ -37,6 +39,8 @@ typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/ typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/ +#define __pyx_PyIndex_AsSsize_t(b) PyInt_AsSsize_t(PyNumber_Index(b)) + #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { if (x == Py_True) return 1; @@ -46,8 +50,14 @@ #ifdef __GNUC__ +/* Test for GCC > 2.95 */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) +#else /* __GNUC__ > 2 ... */ +#define likely(x) (x) +#define unlikely(x) (x) +#endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) @@ -111,13 +121,10 @@ static PyObject *__pyx_k2p; -static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { - PyObject *__pyx_v_datapath = 0; +static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_v_datapath); /*proto*/ +static PyObject *__pyx_f_5_proj_set_datapath(PyObject *__pyx_self, PyObject *__pyx_v_datapath) { char (*__pyx_v_searchpath); PyObject *__pyx_r; - static char *__pyx_argnames[] = {"datapath",0}; - if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "O", __pyx_argnames, &__pyx_v_datapath))) return 0; Py_INCREF(__pyx_v_datapath); /* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":7 @@ -401,15 +408,13 @@ static PyObject *__pyx_n___class__; -static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *unused); /*proto*/ static char __pyx_doc_5_proj_4Proj___reduce__[] = "special method that allows pyproj.Proj instance to be pickled"; -static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { +static PyObject *__pyx_f_5_proj_4Proj___reduce__(PyObject *__pyx_v_self, PyObject *unused) { PyObject *__pyx_r; PyObject *__pyx_1 = 0; PyObject *__pyx_2 = 0; PyObject *__pyx_3 = 0; - static char *__pyx_argnames[] = {0}; - if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0; Py_INCREF(__pyx_v_self); /* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":37 @@ -570,7 +575,7 @@ __pyx_4 = PyNumber_Divide(__pyx_2, __pyx_3); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;} Py_DECREF(__pyx_2); __pyx_2 = 0; Py_DECREF(__pyx_3); __pyx_3 = 0; - __pyx_5 = PyInt_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;} + __pyx_5 = __pyx_PyIndex_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 60; goto __pyx_L1;} Py_DECREF(__pyx_4); __pyx_4 = 0; __pyx_v_ndim = __pyx_5; @@ -922,7 +927,7 @@ __pyx_4 = PyNumber_Divide(__pyx_2, __pyx_3); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;} Py_DECREF(__pyx_2); __pyx_2 = 0; Py_DECREF(__pyx_3); __pyx_3 = 0; - __pyx_5 = PyInt_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;} + __pyx_5 = __pyx_PyIndex_AsSsize_t(__pyx_4); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 106; goto __pyx_L1;} Py_DECREF(__pyx_4); __pyx_4 = 0; __pyx_v_ndim = __pyx_5; @@ -1142,13 +1147,11 @@ return __pyx_r; } -static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { +static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *unused); /*proto*/ +static PyObject *__pyx_f_5_proj_4Proj_is_latlong(PyObject *__pyx_v_self, PyObject *unused) { int __pyx_v_i; PyObject *__pyx_r; int __pyx_1; - static char *__pyx_argnames[] = {0}; - if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0; Py_INCREF(__pyx_v_self); /* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":133 @@ -1203,13 +1206,11 @@ return __pyx_r; } -static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ -static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { +static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *unused); /*proto*/ +static PyObject *__pyx_f_5_proj_4Proj_is_geocent(PyObject *__pyx_v_self, PyObject *unused) { int __pyx_v_i; PyObject *__pyx_r; int __pyx_1; - static char *__pyx_argnames[] = {0}; - if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0; Py_INCREF(__pyx_v_self); /* "/Volumes/User/jwhitaker/python/pyproj/_proj.pyx":142 @@ -1609,7 +1610,7 @@ * if not radians and p2.is_latlong(): * for i from 0 <= i < npts: */ - __pyx_7 = PyInt_AsLong(__pyx_v_ierr); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;} + __pyx_7 = PyInt_AsLong(__pyx_v_ierr); if (unlikely((__pyx_7 == -1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;} __pyx_4 = PyString_FromString(pj_strerrno(__pyx_7)); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;} __pyx_3 = PyTuple_New(1); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; goto __pyx_L1;} PyTuple_SET_ITEM(__pyx_3, 0, __pyx_4); @@ -1791,11 +1792,11 @@ } static struct PyMethodDef __pyx_methods_5_proj_Proj[] = { - {"__reduce__", (PyCFunction)__pyx_f_5_proj_4Proj___reduce__, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj___reduce__}, + {"__reduce__", (PyCFunction)__pyx_f_5_proj_4Proj___reduce__, METH_NOARGS, __pyx_doc_5_proj_4Proj___reduce__}, {"_fwd", (PyCFunction)__pyx_f_5_proj_4Proj__fwd, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj__fwd}, {"_inv", (PyCFunction)__pyx_f_5_proj_4Proj__inv, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_proj_4Proj__inv}, - {"is_latlong", (PyCFunction)__pyx_f_5_proj_4Proj_is_latlong, METH_VARARGS|METH_KEYWORDS, 0}, - {"is_geocent", (PyCFunction)__pyx_f_5_proj_4Proj_is_geocent, METH_VARARGS|METH_KEYWORDS, 0}, + {"is_latlong", (PyCFunction)__pyx_f_5_proj_4Proj_is_latlong, METH_NOARGS, 0}, + {"is_geocent", (PyCFunction)__pyx_f_5_proj_4Proj_is_geocent, METH_NOARGS, 0}, {0, 0, 0, 0} }; @@ -1924,7 +1925,7 @@ }; static struct PyMethodDef __pyx_methods[] = { - {"set_datapath", (PyCFunction)__pyx_f_5_proj_set_datapath, METH_VARARGS|METH_KEYWORDS, 0}, + {"set_datapath", (PyCFunction)__pyx_f_5_proj_set_datapath, METH_O, 0}, {"_transform", (PyCFunction)__pyx_f_5_proj__transform, METH_VARARGS|METH_KEYWORDS, 0}, {0, 0, 0, 0} }; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2007-09-12 18:14:42
|
Revision: 3840 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3840&view=rev Author: jswhit Date: 2007-09-12 11:14:38 -0700 (Wed, 12 Sep 2007) Log Message: ----------- fix isnan check Modified Paths: -------------- trunk/toolkits/basemap/src/_geod.c trunk/toolkits/basemap/src/_geod.pyx Modified: trunk/toolkits/basemap/src/_geod.c =================================================================== --- trunk/toolkits/basemap/src/_geod.c 2007-09-12 17:25:19 UTC (rev 3839) +++ trunk/toolkits/basemap/src/_geod.c 2007-09-12 18:14:38 UTC (rev 3840) @@ -1,4 +1,4 @@ -/* Generated by Pyrex 0.9.6.3 on Fri Aug 31 08:42:50 2007 */ +/* Generated by Pyrex 0.9.6.5 on Wed Sep 12 12:12:33 2007 */ #define PY_SSIZE_T_CLEAN #include "Python.h" @@ -12,6 +12,8 @@ #define PY_SSIZE_T_MIN INT_MIN #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) PyInt_AsLong(o) + #define PyNumber_Index(o) PyNumber_Int(o) + #define PyIndex_Check(o) PyNumber_Check(o) #endif #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" @@ -37,6 +39,8 @@ typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/ typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/ +#define __pyx_PyIndex_AsSsize_t(b) PyInt_AsSsize_t(PyNumber_Index(b)) + #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { if (x == Py_True) return 1; @@ -337,15 +341,13 @@ static PyObject *__pyx_n___class__; -static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ +static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *unused); /*proto*/ static char __pyx_doc_5_geod_4Geod___reduce__[] = "special method that allows pyproj.Geod instance to be pickled"; -static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { +static PyObject *__pyx_f_5_geod_4Geod___reduce__(PyObject *__pyx_v_self, PyObject *unused) { PyObject *__pyx_r; PyObject *__pyx_1 = 0; PyObject *__pyx_2 = 0; PyObject *__pyx_3 = 0; - static char *__pyx_argnames[] = {0}; - if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames))) return 0; Py_INCREF(__pyx_v_self); /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":27 @@ -421,6 +423,7 @@ PyObject *__pyx_5 = 0; Py_ssize_t __pyx_6; double __pyx_7; + int __pyx_8; static char *__pyx_argnames[] = {"lons","lats","az","dist","radians",0}; __pyx_v_radians = __pyx_k3; if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOOO|O", __pyx_argnames, &__pyx_v_lons, &__pyx_v_lats, &__pyx_v_az, &__pyx_v_dist, &__pyx_v_radians))) return 0; @@ -571,7 +574,7 @@ __pyx_5 = PyNumber_Divide(__pyx_3, __pyx_4); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;} Py_DECREF(__pyx_3); __pyx_3 = 0; Py_DECREF(__pyx_4); __pyx_4 = 0; - __pyx_6 = PyInt_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;} + __pyx_6 = __pyx_PyIndex_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 51; goto __pyx_L1;} Py_DECREF(__pyx_5); __pyx_5 = 0; __pyx_v_ndim = __pyx_6; @@ -781,7 +784,7 @@ * geod_for(&self.geodesic_t) * if pj_errno != 0: # <<<<<<<<<<<<<< * raise RuntimeError(pj_strerrno(pj_errno)) - * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: + * if isnan(self.geodesic_t.ALPHA21): */ __pyx_1 = (pj_errno != 0); if (__pyx_1) { @@ -790,7 +793,7 @@ * geod_for(&self.geodesic_t) * if pj_errno != 0: * raise RuntimeError(pj_strerrno(pj_errno)) # <<<<<<<<<<<<<< - * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: + * if isnan(self.geodesic_t.ALPHA21): * raise ValueError('undefined forward geodesic (may be an equatorial arc)') */ __pyx_3 = PyString_FromString(pj_strerrno(pj_errno)); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 72; goto __pyx_L1;} @@ -809,16 +812,16 @@ /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":73 * if pj_errno != 0: * raise RuntimeError(pj_strerrno(pj_errno)) - * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: # <<<<<<<<<<<<<< + * if isnan(self.geodesic_t.ALPHA21): # <<<<<<<<<<<<<< * raise ValueError('undefined forward geodesic (may be an equatorial arc)') * if radians: */ - __pyx_2 = (isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.ALPHA21) == FP_NAN); - if (__pyx_2) { + __pyx_8 = isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.ALPHA21); + if (__pyx_8) { /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":74 * raise RuntimeError(pj_strerrno(pj_errno)) - * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: + * if isnan(self.geodesic_t.ALPHA21): * raise ValueError('undefined forward geodesic (may be an equatorial arc)') # <<<<<<<<<<<<<< * if radians: * lonsdata[i] = self.geodesic_t.p2.v @@ -836,14 +839,14 @@ __pyx_L12:; /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":75 - * if isnan(self.geodesic_t.ALPHA21) == FP_NAN: + * if isnan(self.geodesic_t.ALPHA21): * raise ValueError('undefined forward geodesic (may be an equatorial arc)') * if radians: # <<<<<<<<<<<<<< * lonsdata[i] = self.geodesic_t.p2.v * latsdata[i] = self.geodesic_t.p2.u */ - __pyx_1 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 75; goto __pyx_L1;} - if (__pyx_1) { + __pyx_2 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_2 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 75; goto __pyx_L1;} + if (__pyx_2) { /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":76 * raise ValueError('undefined forward geodesic (may be an equatorial arc)') @@ -980,6 +983,7 @@ PyObject *__pyx_5 = 0; Py_ssize_t __pyx_6; double __pyx_7; + int __pyx_8; static char *__pyx_argnames[] = {"lons1","lats1","lons2","lats2","radians",0}; __pyx_v_radians = __pyx_k4; if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "OOOO|O", __pyx_argnames, &__pyx_v_lons1, &__pyx_v_lats1, &__pyx_v_lons2, &__pyx_v_lats2, &__pyx_v_radians))) return 0; @@ -1130,7 +1134,7 @@ __pyx_5 = PyNumber_Divide(__pyx_3, __pyx_4); if (unlikely(!__pyx_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;} Py_DECREF(__pyx_3); __pyx_3 = 0; Py_DECREF(__pyx_4); __pyx_4 = 0; - __pyx_6 = PyInt_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;} + __pyx_6 = __pyx_PyIndex_AsSsize_t(__pyx_5); if (unlikely(PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 105; goto __pyx_L1;} Py_DECREF(__pyx_5); __pyx_5 = 0; __pyx_v_ndim = __pyx_6; @@ -1281,7 +1285,7 @@ * self.geodesic_t.p2.v = _dg2rad*azdata[i] * self.geodesic_t.p2.u = _dg2rad*distdata[i] # <<<<<<<<<<<<<< * geod_inv(&self.geodesic_t) - * if isnan(self.geodesic_t.DIST) == FP_NAN: + * if isnan(self.geodesic_t.DIST): */ __pyx_3 = __Pyx_GetName(__pyx_m, __pyx_n__dg2rad); if (unlikely(!__pyx_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; goto __pyx_L1;} __pyx_4 = PyFloat_FromDouble((__pyx_v_distdata[__pyx_v_i])); if (unlikely(!__pyx_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 120; goto __pyx_L1;} @@ -1298,7 +1302,7 @@ * self.geodesic_t.p2.v = _dg2rad*azdata[i] * self.geodesic_t.p2.u = _dg2rad*distdata[i] * geod_inv(&self.geodesic_t) # <<<<<<<<<<<<<< - * if isnan(self.geodesic_t.DIST) == FP_NAN: + * if isnan(self.geodesic_t.DIST): * raise ValueError('undefined inverse geodesic (may be an antipodal point)') */ geod_inv((&((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t)); @@ -1306,16 +1310,16 @@ /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":122 * self.geodesic_t.p2.u = _dg2rad*distdata[i] * geod_inv(&self.geodesic_t) - * if isnan(self.geodesic_t.DIST) == FP_NAN: # <<<<<<<<<<<<<< + * if isnan(self.geodesic_t.DIST): # <<<<<<<<<<<<<< * raise ValueError('undefined inverse geodesic (may be an antipodal point)') * if pj_errno != 0: */ - __pyx_2 = (isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.DIST) == FP_NAN); - if (__pyx_2) { + __pyx_8 = isnan(((struct __pyx_obj_5_geod_Geod *)__pyx_v_self)->geodesic_t.DIST); + if (__pyx_8) { /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":123 * geod_inv(&self.geodesic_t) - * if isnan(self.geodesic_t.DIST) == FP_NAN: + * if isnan(self.geodesic_t.DIST): * raise ValueError('undefined inverse geodesic (may be an antipodal point)') # <<<<<<<<<<<<<< * if pj_errno != 0: * raise RuntimeError(pj_strerrno(pj_errno)) @@ -1333,14 +1337,14 @@ __pyx_L10:; /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":124 - * if isnan(self.geodesic_t.DIST) == FP_NAN: + * if isnan(self.geodesic_t.DIST): * raise ValueError('undefined inverse geodesic (may be an antipodal point)') * if pj_errno != 0: # <<<<<<<<<<<<<< * raise RuntimeError(pj_strerrno(pj_errno)) * if radians: */ - __pyx_1 = (pj_errno != 0); - if (__pyx_1) { + __pyx_2 = (pj_errno != 0); + if (__pyx_2) { /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":125 * raise ValueError('undefined inverse geodesic (may be an antipodal point)') @@ -1369,8 +1373,8 @@ * lonsdata[i] = self.geodesic_t.ALPHA12 * latsdata[i] = self.geodesic_t.ALPHA21 */ - __pyx_2 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_2 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 126; goto __pyx_L1;} - if (__pyx_2) { + __pyx_1 = PyObject_IsTrue(__pyx_v_radians); if (unlikely(__pyx_1 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 126; goto __pyx_L1;} + if (__pyx_1) { /* "/Volumes/User/jwhitaker/python/pyproj/_geod.pyx":127 * raise RuntimeError(pj_strerrno(pj_errno)) @@ -1868,7 +1872,7 @@ } static struct PyMethodDef __pyx_methods_5_geod_Geod[] = { - {"__reduce__", (PyCFunction)__pyx_f_5_geod_4Geod___reduce__, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod___reduce__}, + {"__reduce__", (PyCFunction)__pyx_f_5_geod_4Geod___reduce__, METH_NOARGS, __pyx_doc_5_geod_4Geod___reduce__}, {"_fwd", (PyCFunction)__pyx_f_5_geod_4Geod__fwd, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__fwd}, {"_inv", (PyCFunction)__pyx_f_5_geod_4Geod__inv, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__inv}, {"_npts", (PyCFunction)__pyx_f_5_geod_4Geod__npts, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5_geod_4Geod__npts}, Modified: trunk/toolkits/basemap/src/_geod.pyx =================================================================== --- trunk/toolkits/basemap/src/_geod.pyx 2007-09-12 17:25:19 UTC (rev 3839) +++ trunk/toolkits/basemap/src/_geod.pyx 2007-09-12 18:14:38 UTC (rev 3840) @@ -70,7 +70,7 @@ geod_for(&self.geodesic_t) if pj_errno != 0: raise RuntimeError(pj_strerrno(pj_errno)) - if isnan(self.geodesic_t.ALPHA21) == FP_NAN: + if isnan(self.geodesic_t.ALPHA21): raise ValueError('undefined forward geodesic (may be an equatorial arc)') if radians: lonsdata[i] = self.geodesic_t.p2.v @@ -119,7 +119,7 @@ self.geodesic_t.p2.v = _dg2rad*azdata[i] self.geodesic_t.p2.u = _dg2rad*distdata[i] geod_inv(&self.geodesic_t) - if isnan(self.geodesic_t.DIST) == FP_NAN: + if isnan(self.geodesic_t.DIST): raise ValueError('undefined inverse geodesic (may be an antipodal point)') if pj_errno != 0: raise RuntimeError(pj_strerrno(pj_errno)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-12 17:25:26
|
Revision: 3839 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3839&view=rev Author: mdboom Date: 2007-09-12 10:25:19 -0700 (Wed, 12 Sep 2007) Log Message: ----------- Milestone -- simple_plot.py working with new affine framework (with the exception of dpi propagation) Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/figure.py branches/transforms/lib/matplotlib/lines.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-12 15:41:22 UTC (rev 3838) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 17:25:19 UTC (rev 3839) @@ -33,29 +33,29 @@ self._points = N.asarray(points, N.float_) self.track = False - # JDH: if you define a del method, the garbage collector won't - # destory cyclic references, so make sure you either manage these - # yourself or remove the __del__ after testing - def __del__(self): - if self.track: - print "Bbox::__del__" - #@staticmethod def unit(): - return Bbox([[0,0], [1,1]]) + return Bbox.from_lbrt(0., 0., 1., 1.) unit = staticmethod(unit) #@staticmethod def from_lbwh(left, bottom, width, height): - return Bbox([[left, bottom], [left + width, bottom + height]]) + return Bbox.from_lbrt(left, bottom, left + width, bottom + height) from_lbwh = staticmethod(from_lbwh) #@staticmethod - def from_lbrt(left, bottom, right, top): - return Bbox([[left, bottom], [right, top]]) + def from_lbrt(*args): + points = N.array(args, dtype=N.float_).reshape(2, 2) + return Bbox(points) from_lbrt = staticmethod(from_lbrt) - + def __cmp__(self, other): + # MGDTODO: Totally suboptimal + if isinstance(other, Bbox): + if (self._points == other._points).all(): + return 0 + return -1 + # JDH: the update method will update the box limits from the # existing limits and the new data; it appears here you are just # using the new data. We use an "ignore" flag to specify whether @@ -63,31 +63,18 @@ def update_from_data(self, x, y): self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_) self.invalidate() - if self.track: - print "Bbox::update_from_data", self._points def copy(self): - if self.track: - print "Bbox::copy" return Bbox(self._points.copy()) def __repr__(self): return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ - def __cmp__(self, other): - # MGDTODO: Totally suboptimal - if isinstance(other, Bbox): - return (self._points == other._points).all() - return -1 - # MGDTODO: Probably a more efficient ways to do this... def _get_xmin(self): - if self.track: - print "Bbox::_get_xmin" return self._points[0, 0] def _set_xmin(self, val): - print "Bbox::_set_xmin" self._points[0, 0] = val self.invalidate() xmin = property(_get_xmin, _set_xmin) @@ -150,10 +137,10 @@ height = property(_get_height) def transformed(self, transform): - return Bbox(self.transform(self._points)) + return Bbox(transform(self._points)) def inverse_transformed(self, transform): - return Bbox(self.transform.inverted()(self._points)) + return Bbox(transform.inverted()(self._points)) def get_bounds(self): return (self.xmin, self.ymin, @@ -249,6 +236,14 @@ return "Affine2D(%s)" % repr(self._mtx) __str__ = __repr__ + def __cmp__(self, other): + # MGDTODO: We need to decide if we want deferred transforms + # to be equal to this one + if isinstance(other, Affine2D): + if (self.get_matrix() == other.get_matrix()).all(): + return 0 + return -1 + def _do_invalidation(self): result = self._inverted is None self._inverted = None @@ -380,10 +375,9 @@ if self._mtx is None: x_mtx = self._x.get_matrix() y_mtx = self._y.get_matrix() + # This works because we already know the transforms are + # separable self._mtx = N.vstack([x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]]) -# self._mtx = self.matrix_from_values( -# x_mtx[0,0], 0.0, 0.0, y_mtx[1,1], x_mtx[0,2], y_mtx[1,2]) - print "Blended", x_mtx, y_mtx, self._mtx def is_separable(self): return True @@ -429,8 +423,8 @@ def _make__mtx(self): if self._mtx is None: self._mtx = self._concat( - self._b.get_matrix(), - self._a.get_matrix()) + self._a.get_matrix(), + self._b.get_matrix()) def get_matrix(self): self._make__mtx() @@ -547,12 +541,70 @@ return interval[0] < val and interval[1] > val if __name__ == '__main__': + bbox = Bbox.from_lbrt(10., 15., 20., 25.) + assert bbox.xmin == 10 + assert bbox.ymin == 15 + assert bbox.xmax == 20 + assert bbox.ymax == 25 + + assert N.all(bbox.min == [10, 15]) + assert N.all(bbox.max == [20, 25]) + assert N.all(bbox.intervalx == (10, 20)) + assert N.all(bbox.intervaly == (15, 25)) + + assert bbox.width == 10 + assert bbox.height == 10 + + assert bbox.get_bounds() == (10, 15, 10, 10) + + bbox.intervalx = (11, 21) + bbox.intervaly = (16, 26) + + assert bbox.get_bounds() == (11, 16, 10, 10) + + bbox.xmin = 12 + bbox.ymin = 17 + bbox.xmax = 22 + bbox.ymax = 27 + + assert bbox.get_bounds() == (12, 17, 10, 10) + + bbox = Bbox.from_lbwh(10, 11, 12, 13) + assert bbox.get_bounds() == (10, 11, 12, 13) + + bbox_copy = bbox.copy() + assert bbox == bbox_copy + bbox_copy.max = (14, 15) + assert bbox.get_bounds() == (10, 11, 12, 13) + assert bbox_copy.get_bounds() == (10, 11, 4, 4) + bbox1 = Bbox([[10., 15.], [20., 25.]]) bbox2 = Bbox([[30., 35.], [40., 45.]]) trans = BboxTransform(bbox1, bbox2) - print trans(bbox1._points) + bbox3 = bbox1.transformed(trans) + assert bbox3 == bbox2 - bbox2.intervalx = 50, 55 - print trans(bbox1._points) + translation = Affine2D().translate(10, 20) + assert translation.to_values() == (1, 0, 0, 1, 10, 20) + scale = Affine2D().scale(10, 20) + assert scale.to_values() == (10, 0, 0, 20, 0, 0) + rotation = Affine2D().rotate_deg(30) + print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, -0.49999999999999994, 0.86602540378443871, 0.0, 0.0) + points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_) + translated_points = translation(points) + assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all() + scaled_points = scale(points) + print scaled_points + rotated_points = rotation(points) + print rotated_points + + tpoints1 = rotation(translation(scale(points))) + trans_sum = rotation + translation + scale + tpoints2 = trans_sum(points) + print tpoints1, tpoints2 + print tpoints1 == tpoints2 + # Need to do some sort of fuzzy comparison here? + # assert (tpoints1 == tpoints2).all() + __all__ = ['Transform', 'Affine2D'] Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-09-12 15:41:22 UTC (rev 3838) +++ branches/transforms/lib/matplotlib/axes.py 2007-09-12 17:25:19 UTC (rev 3839) @@ -653,15 +653,12 @@ self.viewLim = Bbox.from_lbrt(left, bottom, right, top) self.dataLim = Bbox.unit() - self.dataLim.track = True self.transData = maffine.BboxTransform( self.viewLim, self.bbox) self.transAxes = maffine.BboxTransform( Bbox.unit(), self.bbox) - print "_set_lim_and_transforms", self.viewLim, self.transData, self.dataLim, self.transAxes, self.bbox - # MGDTODO # if self._sharex: # self.transData.set_funcx(self._sharex.transData.get_funcx()) @@ -697,7 +694,6 @@ # # Change values within self._position--don't replace it. # for num,val in zip(pos, self._position): # val.set(num) - print "set_position", self._position, pos self._position = pos # MGDTODO: side-effects if which in ('both', 'original'): @@ -1182,9 +1178,7 @@ #print type(x), type(y) # MGDTODO ## self.dataLim.update_numerix(x, y, -1) - print "update_datalim_numerix", self.dataLim, self.dataLim.update_from_data(x, y) - print self.dataLim def _get_verts_in_data_coords(self, trans, xys): if trans == self.transData: @@ -1245,8 +1239,6 @@ axis direction reversal that has already been done. """ # if image data only just use the datalim - print "autoscale_view", self._autoscaleon, scalex, scaley - if not self._autoscaleon: return if (tight or (len(self.images)>0 and len(self.lines)==0 and @@ -1274,7 +1266,7 @@ def draw(self, renderer=None, inframe=False): "Draw everything (plot lines, axes, labels)" - if renderer is None: + if renderer is None: renderer = self._cachedRenderer if renderer is None: @@ -1550,7 +1542,6 @@ xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False) self.viewLim.intervalx = (xmin, xmax) - print 'set_xlim', self.viewLim, xmin, xmax return xmin, xmax @@ -1654,7 +1645,6 @@ ACCEPTS: len(2) sequence of floats """ - print "set_ylim", ymin, ymax, emit if ymax is None and iterable(ymin): ymin,ymax = ymin @@ -1676,7 +1666,6 @@ ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False) self.viewLim.intervaly = (ymin, ymax) if emit: self.callbacks.process('ylim_changed', self) - print "set_ylim", self.viewLim return ymin, ymax Modified: branches/transforms/lib/matplotlib/figure.py =================================================================== --- branches/transforms/lib/matplotlib/figure.py 2007-09-12 15:41:22 UTC (rev 3838) +++ branches/transforms/lib/matplotlib/figure.py 2007-09-12 17:25:19 UTC (rev 3839) @@ -326,7 +326,6 @@ dpival = self.dpi self.bbox.max = w * dpival, h * dpival - print self.bbox # self.figwidth.set(w) MGDTODO # self.figheight.set(h) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-12 15:41:22 UTC (rev 3838) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-12 17:25:19 UTC (rev 3839) @@ -389,7 +389,6 @@ def set_axes(self, ax): - print "set_axes" Artist.set_axes(self, ax) if ax.xaxis is not None: self._xcid = ax.xaxis.callbacks.connect('units', self.recache) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-09-12 15:41:28
|
Revision: 3838 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3838&view=rev Author: jdh2358 Date: 2007-09-12 08:41:22 -0700 (Wed, 12 Sep 2007) Log Message: ----------- removed Interval from canonical tickers and formatters Modified Paths: -------------- branches/transforms/lib/matplotlib/affine.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/ticker.py Modified: branches/transforms/lib/matplotlib/affine.py =================================================================== --- branches/transforms/lib/matplotlib/affine.py 2007-09-12 14:46:03 UTC (rev 3837) +++ branches/transforms/lib/matplotlib/affine.py 2007-09-12 15:41:22 UTC (rev 3838) @@ -33,6 +33,9 @@ self._points = N.asarray(points, N.float_) self.track = False + # JDH: if you define a del method, the garbage collector won't + # destory cyclic references, so make sure you either manage these + # yourself or remove the __del__ after testing def __del__(self): if self.track: print "Bbox::__del__" @@ -52,6 +55,11 @@ return Bbox([[left, bottom], [right, top]]) from_lbrt = staticmethod(from_lbrt) + + # JDH: the update method will update the box limits from the + # existing limits and the new data; it appears here you are just + # using the new data. We use an "ignore" flag to specify whether + # you want to include the existing data or not in the update def update_from_data(self, x, y): self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], N.float_) self.invalidate() Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-09-12 14:46:03 UTC (rev 3837) +++ branches/transforms/lib/matplotlib/axis.py 2007-09-12 15:41:22 UTC (rev 3838) @@ -855,9 +855,9 @@ ACCEPTS: A Formatter instance """ self.major.formatter = formatter - self.major.formatter.set_view_interval( self.get_view_interval() ) - self.major.formatter.set_data_interval( self.get_data_interval() ) + self.major.formatter.set_axis(self) + def set_minor_formatter(self, formatter): """ Set the formatter of the minor ticker @@ -865,8 +865,7 @@ ACCEPTS: A Formatter instance """ self.minor.formatter = formatter - self.minor.formatter.set_view_interval( self.get_view_interval() ) - self.minor.formatter.set_data_interval( self.get_data_interval() ) + self.minor.formatter.set_axis(self) def set_major_locator(self, locator): @@ -876,8 +875,7 @@ ACCEPTS: a Locator instance """ self.major.locator = locator - self.major.locator.set_view_interval( self.get_view_interval() ) - self.major.locator.set_data_interval( self.get_data_interval() ) + self.major.locator.set_axis(self) def set_minor_locator(self, locator): @@ -887,8 +885,7 @@ ACCEPTS: a Locator instance """ self.minor.locator = locator - self.minor.locator.set_view_interval( self.get_view_interval() ) - self.minor.locator.set_data_interval( self.get_data_interval() ) + self.minor.locator.set_axis(self) def set_pickradius(self, pickradius): """ Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-09-12 14:46:03 UTC (rev 3837) +++ branches/transforms/lib/matplotlib/ticker.py 2007-09-12 15:41:22 UTC (rev 3838) @@ -99,13 +99,7 @@ minor ticks. See the matplotlib.dates module for more information and examples of using date locators and formatters. -DEVELOPERS NOTE -If you are implementing your own class or modifying one of these, it -is critical that you use viewlim and dataInterval READ ONLY MODE so -multiple axes can share the same locator w/o side effects! - - """ @@ -121,37 +115,11 @@ class TickHelper: + axis = None + def set_axis(self, axis): + self.axis = axis - viewInterval = None - dataInterval = None - def verify_intervals(self): - if self.dataInterval is None: - raise RuntimeError("You must set the data interval to use this function") - - if self.viewInterval is None: - raise RuntimeError("You must set the view interval to use this function") - - - def set_view_interval(self, interval): - self.viewInterval = interval - - def set_data_interval(self, interval): - self.dataInterval = interval - - def set_bounds(self, vmin, vmax): - ''' - Set dataInterval and viewInterval from numeric vmin, vmax. - - This is for stand-alone use of Formatters and/or - Locators that require these intervals; that is, for - cases where the Intervals do not need to be updated - automatically. - ''' - # MGDTODO: Interval no longer exists - self.dataInterval = maffine.Interval([vmin, vmax]) - self.viewInterval = maffine.Interval([vmin, vmax]) - class Formatter(TickHelper): """ Convert the tick location to a string @@ -341,9 +309,8 @@ 'set the locations of the ticks' self.locs = locs if len(self.locs) > 0: - self.verify_intervals() - vmin, vmax = self.viewInterval - d = abs(vmax - vmin) + vmin, vmax = self.axis.get_view_interval() + d = abs(vmax-vmin) if self._useOffset: self._set_offset(d) self._set_orderOfMagnitude(d) self._set_format() @@ -865,14 +832,12 @@ def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval + vmin, vmax = self.axis.get_view_interval() vmin, vmax = maffine.nonsingular(vmin, vmax, expander = 0.05) return self.bin_boundaries(vmin, vmax) def autoscale(self): - self.verify_intervals() - dmin, dmax = self.dataInterval + dmin, dmax = self.axis.get_data_interval() dmin, dmax = maffine.nonsingular(dmin, dmax, expander = 0.05) return npy.take(self.bin_boundaries(dmin, dmax), [0,-1]) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-09-12 14:46:06
|
Revision: 3837 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3837&view=rev Author: mdboom Date: 2007-09-12 07:46:03 -0700 (Wed, 12 Sep 2007) Log Message: ----------- Adding pbox.py Added Paths: ----------- branches/transforms/lib/matplotlib/pbox.py Added: branches/transforms/lib/matplotlib/pbox.py =================================================================== --- branches/transforms/lib/matplotlib/pbox.py (rev 0) +++ branches/transforms/lib/matplotlib/pbox.py 2007-09-12 14:46:03 UTC (rev 3837) @@ -0,0 +1,126 @@ +# MGDTODO: Just included verbatim for now + +class PBox(list): + ''' + A left-bottom-width-height (lbwh) specification of a bounding box, + such as is used to specify the position of an Axes object within + a Figure. + It is a 4-element list with methods for changing the size, shape, + and position relative to its container. + ''' + coefs = {'C': (0.5, 0.5), + 'SW': (0,0), + 'S': (0.5, 0), + 'SE': (1.0, 0), + 'E': (1.0, 0.5), + 'NE': (1.0, 1.0), + 'N': (0.5, 1.0), + 'NW': (0, 1.0), + 'W': (0, 0.5)} + def __init__(self, box, container=None, llur=False): + if len(box) != 4: + raise ValueError("Argument must be iterable of length 4") + if llur: + box = [box[0], box[1], box[2]-box[0], box[3]-box[1]] + list.__init__(self, box) + self.set_container(container) + + def as_llur(self): + return [self[0], self[1], self[0]+self[2], self[1]+self[3]] + + def set_container(self, box=None): + if box is None: + box = self + if len(box) != 4: + raise ValueError("Argument must be iterable of length 4") + self._container = list(box) + + def get_container(self, box): + return self._container + + def anchor(self, c, container=None): + ''' + Shift to position c within its container. + + c can be a sequence (cx, cy) where cx, cy range from 0 to 1, + where 0 is left or bottom and 1 is right or top. + + Alternatively, c can be a string: C for centered, + S for bottom-center, SE for bottom-left, E for left, etc. + + Optional arg container is the lbwh box within which the + PBox is positioned; it defaults to the initial + PBox. + ''' + if container is None: + container = self._container + l,b,w,h = container + if isinstance(c, str): + cx, cy = self.coefs[c] + else: + cx, cy = c + W,H = self[2:] + self[:2] = l + cx * (w-W), b + cy * (h-H) + return self + + def shrink(self, mx, my): + ''' + Shrink the box by mx in the x direction and my in the y direction. + The lower left corner of the box remains unchanged. + Normally mx and my will be <= 1, but this is not enforced. + ''' + assert mx >= 0 and my >= 0 + self[2:] = mx * self[2], my * self[3] + return self + + def shrink_to_aspect(self, box_aspect, fig_aspect = 1): + ''' + Shrink the box so that it is as large as it can be while + having the desired aspect ratio, box_aspect. + If the box coordinates are relative--that is, fractions of + a larger box such as a figure--then the physical aspect + ratio of that figure is specified with fig_aspect, so + that box_aspect can also be given as a ratio of the + absolute dimensions, not the relative dimensions. + ''' + assert box_aspect > 0 and fig_aspect > 0 + l,b,w,h = self._container + H = w * box_aspect/fig_aspect + if H <= h: + W = w + else: + W = h * fig_aspect/box_aspect + H = h + self[2:] = W,H + return self + + def splitx(self, *args): + ''' + e.g., PB.splitx(f1, f2, ...) + + Returns a list of new PBoxes formed by + splitting the original one (PB) with vertical lines + at fractional positions f1, f2, ... + ''' + boxes = [] + xf = [0] + list(args) + [1] + l,b,w,h = self[:] + for xf0, xf1 in zip(xf[:-1], xf[1:]): + boxes.append(PBox([l+xf0*w, b, (xf1-xf0)*w, h])) + return boxes + + def splity(self, *args): + ''' + e.g., PB.splity(f1, f2, ...) + + Returns a list of new PBoxes formed by + splitting the original one (PB) with horizontal lines + at fractional positions f1, f2, ..., with y measured + positive up. + ''' + boxes = [] + yf = [0] + list(args) + [1] + l,b,w,h = self[:] + for yf0, yf1 in zip(yf[:-1], yf[1:]): + boxes.append(PBox([l, b+yf0*h, w, (yf1-yf0)*h])) + return boxes This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |