From: <md...@us...> - 2007-11-15 15:18:46
|
Revision: 4305 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4305&view=rev Author: mdboom Date: 2007-11-15 07:18:42 -0800 (Thu, 15 Nov 2007) Log Message: ----------- Speed improvements. Modified Paths: -------------- branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/transforms.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_path.cpp Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-11-15 15:15:21 UTC (rev 4304) +++ branches/transforms/lib/matplotlib/path.py 2007-11-15 15:18:42 UTC (rev 4305) @@ -160,31 +160,33 @@ def iter_segments(self): """ - Iterates over all of the endpoints in the path. Unlike - iterating directly over the vertices array, curve control - points are skipped over. + Iterates over all of the curve segments in the path. """ - i = 0 - NUM_VERTICES = self.NUM_VERTICES vertices = self.vertices codes = self.codes + len_vertices = len(vertices) + NUM_VERTICES = self.NUM_VERTICES + MOVETO = self.MOVETO + LINETO = self.LINETO + CLOSEPOLY = self.CLOSEPOLY + STOP = self.STOP + if not len(vertices): return if codes is None: - code = self.MOVETO - yield vertices[0], self.MOVETO - i = 1 + yield vertices[0], MOVETO for v in vertices[1:]: - yield v, self.LINETO + yield v, LINETO else: - while i < len(vertices): + i = 0 + while i < len_vertices: code = codes[i] - if code == self.CLOSEPOLY: + if code == CLOSEPOLY: yield [], code i += 1 - elif code == self.STOP: + elif code == STOP: return else: num_vertices = NUM_VERTICES[code] Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-11-15 15:15:21 UTC (rev 4304) +++ branches/transforms/lib/matplotlib/transforms.py 2007-11-15 15:18:42 UTC (rev 4305) @@ -25,6 +25,7 @@ import numpy as npy from matplotlib.numerix import npyma as ma +from matplotlib._path import affine_transform from numpy.linalg import inv from weakref import WeakKeyDictionary @@ -1206,10 +1207,13 @@ mtx = self.get_matrix() if ma.isMaskedArray(points): points = ma.dot(mtx[0:2, 0:2], points.transpose()) + mtx[0:2, 2:] - else: - points = npy.dot(mtx[0:2, 0:2], points.transpose()) + mtx[0:2, 2:] - return points.transpose() + return points.transpose() + return affine_transform(points, mtx) + def transform_point(self, point): + mtx = self.get_matrix() + return affine_transform(point, mtx) + if DEBUG: _transform = transform def transform(self, points): @@ -1251,7 +1255,7 @@ Affine2DBase.__init__(self) if matrix is None: matrix = npy.identity(3) - else: + elif DEBUG: matrix = npy.asarray(matrix, npy.float_) assert matrix.shape == (3, 3) self._mtx = matrix @@ -1276,7 +1280,9 @@ b d f 0 0 1 """ - return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f)) + return Affine2D( + npy.array([a, c, e, b, d, f, 0.0, 0.0, 1.0], npy.float_) + .reshape((3,3))) from_values = staticmethod(from_values) def get_matrix(self): Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-11-15 15:15:21 UTC (rev 4304) +++ branches/transforms/src/_backend_agg.cpp 2007-11-15 15:18:42 UTC (rev 4305) @@ -282,8 +282,8 @@ double l, b, r, t; if (py_convert_bbox(cliprect.ptr(), l, b, r, t)) { - rasterizer->clip_box(int(round(l)) + 1, height - int(round(b)) + 1, - int(round(r)) + 1, height - int(round(t)) + 1); + rasterizer->clip_box(int(round(l)) + 1, height - int(round(b)), + int(round(r)), height - int(round(t))); } _VERBOSE("RendererAgg::set_clipbox done"); @@ -526,8 +526,6 @@ strokeCache = new agg::int8u[strokeSize]; // or any container scanlines.serialize(strokeCache); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); set_clipbox(gc.cliprect, rendererBase); bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans); @@ -536,34 +534,41 @@ agg::serialized_scanlines_adaptor_aa8 sa; agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; - while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { - //render the fill - if (face.first) { - if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); + if (face.first) { + // render the fill + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(face.second); + while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { sa.init(fillCache, fillSize, x, y); - ren.color(face.second); agg::render_scanlines(sa, sl, ren); - } else { + } + } else { + rendererAA->color(face.second); + while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { sa.init(fillCache, fillSize, x, y); - rendererAA->color(face.second); agg::render_scanlines(sa, sl, *rendererAA); } } - - //render the stroke - if (has_clippath) { - pixfmt_amask_type pfa(*pixFmt, *alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); + path_quantized.rewind(0); + } + + //render the stroke + if (has_clippath) { + pixfmt_amask_type pfa(*pixFmt, *alphaMask); + amask_ren_type r(pfa); + amask_aa_renderer_type ren(r); + ren.color(gc.color); + while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { sa.init(strokeCache, strokeSize, x, y); - ren.color(gc.color); agg::render_scanlines(sa, sl, ren); - } else { + } + } else { + rendererAA->color(gc.color); + while (path_quantized.vertex(&x, &y) != agg::path_cmd_stop) { sa.init(strokeCache, strokeSize, x, y); - rendererAA->color(gc.color); agg::render_scanlines(sa, sl, *rendererAA); } } @@ -880,8 +885,6 @@ GCAgg gc = GCAgg(gc_obj, dpi); facepair_t face = _get_rgba_face(face_obj, gc.alpha); - theRasterizer->reset_clipping(); - rendererBase->reset_clipping(true); set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans); Modified: branches/transforms/src/_path.cpp =================================================================== --- branches/transforms/src/_path.cpp 2007-11-15 15:15:21 UTC (rev 4304) +++ branches/transforms/src/_path.cpp 2007-11-15 15:18:42 UTC (rev 4305) @@ -11,6 +11,13 @@ // MGDTODO: Un-CXX-ify this module +struct XY { + double x; + double y; + + XY(double x_, double y_) : x(x_), y(y_) {} +}; + // the extension module class _path_module : public Py::ExtensionModule<_path_module> { @@ -32,7 +39,9 @@ "point_in_path_collection(a, atrans, b, btrans)"); add_varargs_method("clip_path_to_rect", &_path_module::clip_path_to_rect, "clip_path_to_rect(path, bbox, inside)"); - + add_varargs_method("affine_transform", &_path_module::affine_transform, + "affine_transform(vertices, transform)"); + initialize("Helper functions for paths"); } @@ -47,6 +56,7 @@ Py::Object point_in_path_collection(const Py::Tuple& args); Py::Object path_in_path(const Py::Tuple& args); Py::Object clip_path_to_rect(const Py::Tuple& args); + Py::Object affine_transform(const Py::Tuple& args); }; // @@ -97,7 +107,7 @@ inside_flag = 0; unsigned code = 0; - while (true) { + do { if (code != agg::path_cmd_move_to) code = path.vertex(&x, &y); @@ -111,7 +121,7 @@ vty1 = x; inside_flag = 0; - while (true) { + do { code = path.vertex(&x, &y); // The following cases denote the beginning on a new subpath @@ -151,12 +161,9 @@ vtx1 = x; vty1 = y; + } while (code != agg::path_cmd_stop && + (code & agg::path_cmd_end_poly) != agg::path_cmd_end_poly); - if (code == agg::path_cmd_stop || - (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) - break; - } - yflag1 = (vty1 >= ty); if (yflag0 != yflag1) { if ( ((vty1-ty) * (vtx0-vtx1) >= @@ -167,11 +174,8 @@ if (inside_flag != 0) return true; + } while (code != agg::path_cmd_stop); - if (code == agg::path_cmd_stop) - break; - } - return (inside_flag != 0); } @@ -453,7 +457,7 @@ http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm */ -typedef std::vector<std::pair<double, double> > Polygon; +typedef std::vector<XY> Polygon; namespace clip_to_rect_filters { /* There are four different passes needed to create/remove vertices @@ -476,7 +480,7 @@ struct xlt : public bisectx { xlt(double x) : bisectx(x) {} - bool operator()(double x, double y) const { + bool is_inside(double x, double y) const { return x <= m_x; } }; @@ -484,7 +488,7 @@ struct xgt : public bisectx { xgt(double x) : bisectx(x) {} - bool operator()(double x, double y) const { + bool is_inside(double x, double y) const { return x >= m_x; } }; @@ -505,7 +509,7 @@ struct ylt : public bisecty { ylt(double y) : bisecty(y) {} - bool operator()(double x, double y) const { + bool is_inside(double x, double y) const { return y <= m_y; } }; @@ -513,7 +517,7 @@ struct ygt : public bisecty { ygt(double y) : bisecty(y) {} - bool operator()(double x, double y) const { + bool is_inside(double x, double y) const { return y >= m_y; } }; @@ -528,29 +532,23 @@ if (polygon.size() == 0) return; - sx = polygon.back().first; - sy = polygon.back().second; + sx = polygon.back().x; + sy = polygon.back().y; for (Polygon::const_iterator i = polygon.begin(); i != polygon.end(); ++i) { - px = i->first; - py = i->second; + px = i->x; + py = i->y; - sinside = filter(sx, sy); - pinside = filter(px, py); + sinside = filter.is_inside(sx, sy); + pinside = filter.is_inside(px, py); - if (sinside) { - if (pinside) { - result.push_back(std::make_pair(px, py)); - } else { - filter.bisect(sx, sy, px, py, &bx, &by); - result.push_back(std::make_pair(bx, by)); - } - } else { - if (pinside) { - filter.bisect(sx, sy, px, py, &bx, &by); - result.push_back(std::make_pair(bx, by)); - result.push_back(std::make_pair(px, py)); - } + if (sinside ^ pinside) { + filter.bisect(sx, sy, px, py, &bx, &by); + result.push_back(XY(bx, by)); } + + if (pinside) { + result.push_back(XY(px, py)); + } sx = px; sy = py; } @@ -582,11 +580,12 @@ unsigned code = 0; path.rewind(0); - while (true) { + do { + // Grab the next subpath and store it in polygon1 polygon1.clear(); - while (true) { + do { if (code == agg::path_cmd_move_to) - polygon1.push_back(std::make_pair(x, y)); + polygon1.push_back(XY(x, y)); code = path.vertex(&x, &y); @@ -594,15 +593,9 @@ break; if (code != agg::path_cmd_move_to) - polygon1.push_back(std::make_pair(x, y)); + polygon1.push_back(XY(x, y)); + } while ((code & agg::path_cmd_end_poly) != agg::path_cmd_end_poly); - if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { - break; - } else if (code == agg::path_cmd_move_to) { - break; - } - } - // The result of each step is fed into the next (note the // swapping of polygon1 and polygon2 at each step). clip_to_rect_one_step(polygon1, polygon2, clip_to_rect_filters::xlt(xmax)); @@ -610,12 +603,10 @@ clip_to_rect_one_step(polygon1, polygon2, clip_to_rect_filters::ylt(ymax)); clip_to_rect_one_step(polygon2, polygon1, clip_to_rect_filters::ygt(ymin)); + // Empty polygons aren't very useful, so skip them if (polygon1.size()) results.push_back(polygon1); - - if (code == agg::path_cmd_stop) - break; - } + } while (code != agg::path_cmd_stop); } Py::Object _path_module::clip_path_to_rect(const Py::Tuple &args) { @@ -633,30 +624,118 @@ ::clip_to_rect(path, x0, y0, x1, y1, inside, results); - // MGDTODO: Not exception safe int dims[2]; dims[1] = 2; PyObject* py_results = PyList_New(results.size()); - for (std::vector<Polygon>::const_iterator p = results.begin(); p != results.end(); ++p) { - size_t size = p->size(); - dims[0] = p->size(); - PyArrayObject* pyarray = (PyArrayObject*)PyArray_FromDims(2, dims, PyArray_DOUBLE); - for (size_t i = 0; i < size; ++i) { - ((double *)pyarray->data)[2*i] = (*p)[i].first; - ((double *)pyarray->data)[2*i+1] = (*p)[i].second; + if (!py_results) + throw Py::RuntimeError("Error creating results list"); + try { + for (std::vector<Polygon>::const_iterator p = results.begin(); p != results.end(); ++p) { + size_t size = p->size(); + dims[0] = p->size(); + PyArrayObject* pyarray = (PyArrayObject*)PyArray_FromDims(2, dims, PyArray_DOUBLE); + for (size_t i = 0; i < size; ++i) { + ((double *)pyarray->data)[2*i] = (*p)[i].x; + ((double *)pyarray->data)[2*i+1] = (*p)[i].y; + } + if (PyList_SetItem(py_results, p - results.begin(), (PyObject *)pyarray) != -1) { + throw Py::RuntimeError("Error creating results list"); + } } - // MGDTODO: Error check - PyList_SetItem(py_results, p - results.begin(), (PyObject *)pyarray); + } catch (...) { + Py_XDECREF(py_results); + throw; } return Py::Object(py_results, true); } -struct XY { - double x; - double y; -}; +Py::Object _path_module::affine_transform(const Py::Tuple& args) { + args.verify_length(2); + + Py::Object vertices_obj = args[0]; + Py::Object transform_obj = args[1]; + PyArrayObject* vertices = NULL; + PyArrayObject* transform = NULL; + PyArrayObject* result = NULL; + + try { + vertices = (PyArrayObject*)PyArray_FromObject + (vertices_obj.ptr(), PyArray_DOUBLE, 1, 2); + if (!vertices || + (PyArray_NDIM(vertices) == 2 && PyArray_DIM(vertices, 1) != 2) || + (PyArray_NDIM(vertices) == 1 && PyArray_DIM(vertices, 0) != 2)) + throw Py::ValueError("Invalid vertices array."); + + transform = (PyArrayObject*) PyArray_FromObject + (transform_obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!transform || PyArray_NDIM(transform) != 2 || PyArray_DIM(transform, 0) != 3 || PyArray_DIM(transform, 1) != 3) + throw Py::ValueError("Invalid transform."); + + double a, b, c, d, e, f; + { + size_t stride0 = PyArray_STRIDE(transform, 0); + size_t stride1 = PyArray_STRIDE(transform, 1); + char* row0 = PyArray_BYTES(transform); + 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); + } + + result = (PyArrayObject*)PyArray_FromDims + (PyArray_NDIM(vertices), PyArray_DIMS(vertices), PyArray_DOUBLE); + if (PyArray_NDIM(vertices) == 2) { + size_t n = PyArray_DIM(vertices, 0); + char* vertex_in = PyArray_BYTES(vertices); + double* vertex_out = (double*)PyArray_DATA(result); + size_t stride0 = PyArray_STRIDE(vertices, 0); + size_t stride1 = PyArray_STRIDE(vertices, 1); + double x; + double y; + + for (size_t i = 0; i < n; ++i) { + x = *(double*)(vertex_in); + y = *(double*)(vertex_in + stride1); + + *vertex_out++ = a*x + c*y + e; + *vertex_out++ = b*x + d*y + f; + + vertex_in += stride0; + } + } else { + char* vertex_in = PyArray_BYTES(vertices); + double* vertex_out = (double*)PyArray_DATA(result); + size_t stride0 = PyArray_STRIDE(vertices, 0); + double x; + double y; + x = *(double*)(vertex_in); + y = *(double*)(vertex_in + stride0); + *vertex_out++ = a*x + c*y + e; + *vertex_out++ = b*x + d*y + f; + } + } catch (...) { + Py_XDECREF(vertices); + Py_XDECREF(transform); + Py_XDECREF(result); + } + + Py_XDECREF(vertices); + Py_XDECREF(transform); + + return Py::Object((PyObject*)result, true); +} + extern "C" DL_EXPORT(void) init_path(void) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |