From: <md...@us...> - 2007-10-24 16:17:52
|
Revision: 3992 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3992&view=rev Author: mdboom Date: 2007-10-24 09:03:49 -0700 (Wed, 24 Oct 2007) Log Message: ----------- Separated path utilities from backend_agg Modified Paths: -------------- branches/transforms/lib/matplotlib/backends/backend_pdf.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/path.py branches/transforms/setup.py branches/transforms/setupext.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_backend_agg.h branches/transforms/src/agg_py_path_iterator.h Modified: branches/transforms/lib/matplotlib/backends/backend_pdf.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/lib/matplotlib/backends/backend_pdf.py 2007-10-24 16:03:49 UTC (rev 3992) @@ -35,8 +35,6 @@ from matplotlib.transforms import Bbox, BboxBase from matplotlib.path import Path from matplotlib import ttconv -# MGDTODO: Move this stuff -from matplotlib.backends._backend_agg import get_path_extents # Overview # @@ -1034,8 +1032,7 @@ def writeMarkers(self): for tup in self.markers.values(): name, object, path, trans, fillp, lw = tup - a, b, c, d = get_path_extents(path, trans) - bbox = Bbox.from_lbrt(*get_path_extents(path, trans)) + bbox = Bbox.from_lbrt(*path.get_extents(trans)) bbox = bbox.padded(lw * 0.5) self.beginStream( object.id, None, Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/lib/matplotlib/collections.py 2007-10-24 16:03:49 UTC (rev 3992) @@ -19,13 +19,6 @@ import matplotlib.nxutils as nxutils import matplotlib.path as path -# MGDTODO: Move this stuff -from matplotlib.backends._backend_agg import get_path_collection_extents, \ - point_in_path_collection - -# MGDTODO: Treat facecolors and edgecolors as numpy arrays always -# and then update draw_path_collection to use the array interface - class Collection(artist.Artist, cm.ScalarMappable): """ Base class for Collections. Must be subclassed to be usable. @@ -133,7 +126,7 @@ return self._transforms def get_datalim(self, transData): - result = transforms.Bbox.from_lbrt(*get_path_collection_extents( + result = transforms.Bbox.from_lbrt(*path.get_path_collection_extents( self.get_transform().frozen(), self.get_paths(), self.get_transforms(), @@ -201,7 +194,7 @@ transform = transform.get_affine() # MGDTODO: Don't pick when outside of clip path / clip box - ind = point_in_path_collection( + ind = path.point_in_path_collection( mouseevent.x, mouseevent.y, self._pickradius, transform.frozen(), paths, self.get_transforms(), npy.asarray(self._offsets, npy.float_), Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/lib/matplotlib/path.py 2007-10-24 16:03:49 UTC (rev 3992) @@ -9,7 +9,8 @@ import numpy as npy from numpy import ma as ma -from matplotlib.backends._backend_agg import point_in_path, get_path_extents +from matplotlib._path import point_in_path, get_path_extents, \ + get_path_collection_extents, point_in_path_collection from matplotlib.cbook import simple_linear_interpolation KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0 Modified: branches/transforms/setup.py =================================================================== --- branches/transforms/setup.py 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/setup.py 2007-10-24 16:03:49 UTC (rev 3992) @@ -19,6 +19,11 @@ # whatever array packages you have installed. BUILD_IMAGE = 1 +# Build the path utilities module. This module depends on some parts +# of Agg, but is separate from _backend_agg, since it is used even when +# the Agg renderer is not. +BUILD_PATH = 1 + # Build the antigrain geometry toolkit. Agg makes heavy use of # templates, so it probably requires a fairly recent compiler to build # it. It makes very nice antialiased output and also supports alpha @@ -77,7 +82,7 @@ import glob from distutils.core import setup from setupext import build_agg, build_gtkagg, build_tkagg, build_wxagg,\ - build_ft2font, build_image, build_windowing, \ + build_ft2font, build_image, build_windowing, build_path, \ build_contour, build_nxutils, build_enthought, build_swigagg, build_gdk, \ build_subprocess, build_ttconv, print_line, print_status, print_message, \ print_raw, check_for_freetype, check_for_libpng, check_for_gtk, check_for_tk, \ @@ -260,6 +265,9 @@ if BUILD_IMAGE: build_image(ext_modules, packages) +if BUILD_PATH: + build_path(ext_modules, packages) + for mod in ext_modules: if VERBOSE: mod.extra_compile_args.append('-DVERBOSE') Modified: branches/transforms/setupext.py =================================================================== --- branches/transforms/setupext.py 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/setupext.py 2007-10-24 16:03:49 UTC (rev 3992) @@ -90,6 +90,7 @@ BUILT_ENTHOUGHT = False BUILT_CONTOUR = False BUILT_GDK = False +BUILT_PATH = False AGG_VERSION = 'agg23' @@ -897,6 +898,37 @@ BUILT_AGG = True +def build_path(ext_modules, packages): + global BUILT_PATH + if BUILT_PATH: return # only build it if you you haven't already + + agg = ( + 'agg_curves.cpp', + 'agg_bezier_arc.cpp', + 'agg_path_storage.cpp', + 'agg_trans_affine.cpp', + 'agg_vcgen_stroke.cpp', + ) + + deps = ['%s/src/%s'%(AGG_VERSION, name) for name in agg] + deps.extend(glob.glob('CXX/*.cxx')) + deps.extend(glob.glob('CXX/*.c')) + + temp_copy('src/_path.cpp', 'src/path.cpp') + deps.extend(['src/path.cpp']) + module = Extension( + 'matplotlib._path', + deps, + include_dirs=numpy_inc_dirs, + ) + + add_numpy_flags(module) + + add_agg_flags(module) + ext_modules.append(module) + + BUILT_PATH = True + def build_image(ext_modules, packages): global BUILT_IMAGE if BUILT_IMAGE: return # only build it if you you haven't already Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/src/_backend_agg.cpp 2007-10-24 16:03:49 UTC (rev 3992) @@ -30,6 +30,7 @@ #define PY_ARRAY_TYPES_PREFIX NumPy #include "numpy/arrayobject.h" +#include "agg_py_transforms.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -41,53 +42,7 @@ #define M_PI_2 1.57079632679489661923 #endif -/** 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, bool errors=true) { - PyArrayObject* matrix = NULL; - - try { - if (obj.ptr() == Py_None) - throw Py::Exception(); - matrix = (PyArrayObject*) PyArray_FromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!matrix) - throw Py::Exception(); - if (matrix->nd == 2 || matrix->dimensions[0] == 3 || matrix->dimensions[1] == 3) { - size_t stride0 = matrix->strides[0]; - size_t stride1 = matrix->strides[1]; - char* row0 = matrix->data; - char* row1 = row0 + stride0; - - double a = *(double*)(row0); - row0 += stride1; - double c = *(double*)(row0); - row0 += stride1; - double e = *(double*)(row0); - - double b = *(double*)(row1); - row1 += stride1; - double d = *(double*)(row1); - row1 += stride1; - double f = *(double*)(row1); - - Py_XDECREF(matrix); - - return agg::trans_affine(a, b, c, d, e, f); - } - throw Py::Exception(); - } catch (...) { - if (errors) { - Py_XDECREF(matrix); - throw Py::TypeError("Invalid affine transformation matrix"); - } - } - - Py_XDECREF(matrix); - return agg::trans_affine(); -} - /* Convert dashes from the Python representation as nested sequences to the C++ representation as a std::vector<std::pair<double, double> > @@ -1392,338 +1347,6 @@ } -// -// The following code was found in the Agg 2.3 examples (interactive_polygon.cpp). -// It has been generalized to work on (possibly curved) polylines, rather than -// just polygons. The original comments have been kept intact. -// -- Michael Droettboom 2007-10-02 -// -//======= Crossings Multiply algorithm of InsideTest ======================== -// -// By Eric Haines, 3D/Eye Inc, er...@ey... -// -// This version is usually somewhat faster than the original published in -// Graphics Gems IV; by turning the division for testing the X axis crossing -// into a tricky multiplication test this part of the test became faster, -// which had the additional effect of making the test for "both to left or -// both to right" a bit slower for triangles than simply computing the -// intersection each time. The main increase is in triangle testing speed, -// which was about 15% faster; all other polygon complexities were pretty much -// the same as before. On machines where division is very expensive (not the -// case on the HP 9000 series on which I tested) this test should be much -// faster overall than the old code. Your mileage may (in fact, will) vary, -// depending on the machine and the test data, but in general I believe this -// code is both shorter and faster. This test was inspired by unpublished -// Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson. -// Related work by Samosky is in: -// -// Samosky, Joseph, "SectionView: A system for interactively specifying and -// visualizing sections through three-dimensional medical image data", -// M.S. Thesis, Department of Electrical Engineering and Computer Science, -// Massachusetts Institute of Technology, 1993. -// -// Shoot a test ray along +X axis. The strategy is to compare vertex Y values -// to the testing point's Y and quickly discard edges which are entirely to one -// side of the test ray. Note that CONVEX and WINDING code can be added as -// for the CrossingsTest() code; it is left out here for clarity. -// -// Input 2D polygon _pgon_ with _numverts_ number of vertices and test point -// _point_, returns 1 if inside, 0 if outside. -template<class T> -bool point_in_path_impl(double tx, double ty, T& path) { - int yflag0, yflag1, inside_flag; - double vtx0, vty0, vtx1, vty1, sx, sy; - double x, y; - - path.rewind(0); - unsigned code = path.vertex(&x, &y); - if (code == agg::path_cmd_stop) - return false; - - while (true) { - sx = vtx0 = x; - sy = vty0 = y; - - // get test bit for above/below X axis - yflag0 = (vty0 >= ty); - - vtx1 = x; - vty1 = x; - - inside_flag = 0; - while (true) { - code = path.vertex(&x, &y); - - // The following cases denote the beginning on a new subpath - if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { - x = sx; y = sy; - } else if (code == agg::path_cmd_move_to) - break; - - yflag1 = (vty1 >= ty); - // Check if endpoints straddle (are on opposite sides) of X axis - // (i.e. the Y's differ); if so, +X ray could intersect this edge. - // The old test also checked whether the endpoints are both to the - // right or to the left of the test point. However, given the faster - // intersection point computation used below, this test was found to - // be a break-even proposition for most polygons and a loser for - // triangles (where 50% or more of the edges which survive this test - // will cross quadrants and so have to have the X intersection computed - // anyway). I credit Joseph Samosky with inspiring me to try dropping - // the "both left or both right" part of my code. - if (yflag0 != yflag1) { - // Check intersection of pgon segment with +X ray. - // Note if >= point's X; if so, the ray hits it. - // The division operation is avoided for the ">=" test by checking - // the sign of the first vertex wrto the test point; idea inspired - // by Joseph Samosky's and Mark Haigh-Hutchinson's different - // polygon inclusion tests. - if ( ((vty1-ty) * (vtx0-vtx1) >= - (vtx1-tx) * (vty0-vty1)) == yflag1 ) { - inside_flag ^= 1; - } - } - - // Move to the next pair of vertices, retaining info as possible. - yflag0 = yflag1; - vtx0 = vtx1; - vty0 = vty1; - - vtx1 = x; - vty1 = y; - - if (code == agg::path_cmd_stop || - (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) - break; - } - - if (inside_flag != 0) - return true; - - if (code == agg::path_cmd_stop) - return false; - } - - return false; -} - -bool point_in_path(double x, double y, PathIterator& path, agg::trans_affine& trans) { - typedef agg::conv_transform<PathIterator> transformed_path_t; - typedef agg::conv_curve<transformed_path_t> curve_t; - - transformed_path_t trans_path(path, trans); - curve_t curved_path(trans_path); - return point_in_path_impl(x, y, curved_path); -} - -bool point_on_path(double x, double y, double r, PathIterator& path, agg::trans_affine& trans) { - 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; - - transformed_path_t trans_path(path, trans); - curve_t curved_path(trans_path); - stroke_t stroked_path(curved_path); - stroked_path.width(r * 2.0); - return point_in_path_impl(x, y, stroked_path); -} - -Py::Object _backend_agg_module::point_in_path(const Py::Tuple& args) { - args.verify_length(4); - - double x = Py::Float(args[0]); - double y = Py::Float(args[1]); - PathIterator path(args[2]); - agg::trans_affine trans = py_to_agg_transformation_matrix(args[3]); - - if (::point_in_path(x, y, path, trans)) - return Py::Int(1); - return Py::Int(0); -} - -Py::Object _backend_agg_module::point_on_path(const Py::Tuple& args) { - args.verify_length(5); - - double x = Py::Float(args[0]); - double y = Py::Float(args[1]); - double r = Py::Float(args[2]); - PathIterator path(args[3]); - agg::trans_affine trans = py_to_agg_transformation_matrix(args[4]); - - if (::point_on_path(x, y, r, path, trans)) - return Py::Int(1); - return Py::Int(0); -} - -void get_path_extents(PathIterator& path, agg::trans_affine& trans, - double* x0, double* y0, double* x1, double* y1) { - typedef agg::conv_transform<PathIterator> transformed_path_t; - typedef agg::conv_curve<transformed_path_t> curve_t; - double x, y; - unsigned code; - - transformed_path_t tpath(path, trans); - curve_t curved_path(tpath); - - curved_path.rewind(0); - - while ((code = curved_path.vertex(&x, &y)) != agg::path_cmd_stop) { - if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) - continue; - if (x < *x0) *x0 = x; - if (y < *y0) *y0 = y; - if (x > *x1) *x1 = x; - if (y > *y1) *y1 = y; - } -} - -Py::Object _backend_agg_module::get_path_extents(const Py::Tuple& args) { - args.verify_length(2); - - PathIterator path(args[0]); - agg::trans_affine trans = py_to_agg_transformation_matrix(args[1]); - - double x0 = std::numeric_limits<double>::infinity(); - double y0 = std::numeric_limits<double>::infinity(); - double x1 = -std::numeric_limits<double>::infinity(); - double y1 = -std::numeric_limits<double>::infinity(); - - ::get_path_extents(path, trans, &x0, &y0, &x1, &y1); - - Py::Tuple result(4); - result[0] = Py::Float(x0); - result[1] = Py::Float(y0); - result[2] = Py::Float(x1); - result[3] = Py::Float(y1); - return result; -} - -Py::Object _backend_agg_module::get_path_collection_extents(const Py::Tuple& args) { - args.verify_length(5); - - //segments, trans, clipbox, colors, linewidths, antialiaseds - agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[0]); - Py::SeqBase<Py::Object> paths = args[1]; - Py::SeqBase<Py::Object> transforms_obj = args[2]; - Py::Object offsets_obj = args[3]; - agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[4], false); - - PyArrayObject* offsets = NULL; - double x0, y0, x1, y1; - - try { - offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!offsets || offsets->dimensions[1] != 2) - throw Py::ValueError("Offsets array must be Nx2"); - - size_t Npaths = paths.length(); - size_t Noffsets = offsets->dimensions[0]; - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t i; - - // Convert all of the transforms up front - typedef std::vector<agg::trans_affine> transforms_t; - transforms_t transforms; - transforms.reserve(Ntransforms); - for (i = 0; i < Ntransforms; ++i) { - agg::trans_affine trans = py_to_agg_transformation_matrix - (transforms_obj[i], false); - trans *= master_transform; - transforms.push_back(trans); - } - - // The offset each of those and collect the mins/maxs - x0 = std::numeric_limits<double>::infinity(); - y0 = std::numeric_limits<double>::infinity(); - x1 = -std::numeric_limits<double>::infinity(); - y1 = -std::numeric_limits<double>::infinity(); - for (i = 0; i < N; ++i) { - PathIterator path(paths[i % Npaths]); - - double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); - double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); - offset_trans.transform(&xo, &yo); - agg::trans_affine_translation transOffset(xo, yo); - agg::trans_affine trans = transforms[i % Ntransforms]; - trans *= transOffset; - - ::get_path_extents(path, trans, &x0, &y0, &x1, &y1); - } - } catch (...) { - Py_XDECREF(offsets); - throw; - } - - Py_XDECREF(offsets); - - Py::Tuple result(4); - result[0] = Py::Float(x0); - result[1] = Py::Float(y0); - result[2] = Py::Float(x1); - result[3] = Py::Float(y1); - return result; -} - -Py::Object _backend_agg_module::point_in_path_collection(const Py::Tuple& args) { - args.verify_length(9); - - //segments, trans, clipbox, colors, linewidths, antialiaseds - double x = Py::Float(args[0]); - double y = Py::Float(args[1]); - double radius = Py::Float(args[2]); - agg::trans_affine master_transform = py_to_agg_transformation_matrix(args[3]); - Py::SeqBase<Py::Object> paths = args[4]; - Py::SeqBase<Py::Object> transforms_obj = args[5]; - Py::SeqBase<Py::Object> offsets_obj = args[6]; - agg::trans_affine offset_trans = py_to_agg_transformation_matrix(args[7]); - bool filled = Py::Int(args[8]); - - PyArrayObject* offsets = (PyArrayObject*)PyArray_FromObject(offsets_obj.ptr(), PyArray_DOUBLE, 2, 2); - if (!offsets || offsets->dimensions[1] != 2) - throw Py::ValueError("Offsets array must be Nx2"); - - size_t Npaths = paths.length(); - size_t Noffsets = offsets->dimensions[0]; - size_t N = std::max(Npaths, Noffsets); - size_t Ntransforms = std::min(transforms_obj.length(), N); - size_t i; - - // Convert all of the transforms up front - typedef std::vector<agg::trans_affine> transforms_t; - transforms_t transforms; - transforms.reserve(Ntransforms); - for (i = 0; i < Ntransforms; ++i) { - agg::trans_affine trans = py_to_agg_transformation_matrix - (transforms_obj[i], false); - trans *= master_transform; - transforms.push_back(trans); - } - - Py::List result; - - for (i = 0; i < N; ++i) { - PathIterator path(paths[i % Npaths]); - - double xo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 0); - double yo = *(double*)PyArray_GETPTR2(offsets, i % Noffsets, 1); - offset_trans.transform(&xo, &yo); - agg::trans_affine_translation transOffset(xo, yo); - agg::trans_affine trans = transforms[i % Ntransforms]; - trans *= transOffset; - - if (filled) { - if (::point_in_path(x, y, path, trans)) - result.append(Py::Int((int)i)); - } else { - if (::point_on_path(x, y, radius, path, trans)) - result.append(Py::Int((int)i)); - } - } - - return result; -} - /* ------------ module methods ------------- */ Py::Object _backend_agg_module::new_renderer (const Py::Tuple &args, const Py::Dict &kws) Modified: branches/transforms/src/_backend_agg.h =================================================================== --- branches/transforms/src/_backend_agg.h 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/src/_backend_agg.h 2007-10-24 16:03:49 UTC (rev 3992) @@ -236,17 +236,6 @@ add_keyword_method("RendererAgg", &_backend_agg_module::new_renderer, "RendererAgg(width, height, dpi)"); - add_varargs_method("point_in_path", &_backend_agg_module::point_in_path, - "point_in_path(x, y, path, trans)"); - add_varargs_method("point_on_path", &_backend_agg_module::point_on_path, - "point_on_path(x, y, r, path, trans)"); - add_varargs_method("get_path_extents", &_backend_agg_module::get_path_extents, - "get_path_extents(path, trans)"); - add_varargs_method("get_path_collection_extents", &_backend_agg_module::get_path_collection_extents, - "get_path_collection_extents(trans, paths, transforms, offsets, offsetTrans)"); - add_varargs_method("point_in_path_collection", &_backend_agg_module::point_in_path_collection, - "point_in_path_collection(x, y, r, trans, paths, transforms, offsets, offsetTrans, filled)"); - initialize( "The agg rendering backend" ); } @@ -255,11 +244,6 @@ private: Py::Object new_renderer (const Py::Tuple &args, const Py::Dict &kws); - Py::Object point_in_path(const Py::Tuple& args); - Py::Object point_on_path(const Py::Tuple& args); - Py::Object get_path_extents(const Py::Tuple& args); - Py::Object get_path_collection_extents(const Py::Tuple& args); - Py::Object point_in_path_collection(const Py::Tuple& args); }; Modified: branches/transforms/src/agg_py_path_iterator.h =================================================================== --- branches/transforms/src/agg_py_path_iterator.h 2007-10-23 21:25:24 UTC (rev 3991) +++ branches/transforms/src/agg_py_path_iterator.h 2007-10-24 16:03:49 UTC (rev 3992) @@ -1,5 +1,10 @@ +#ifndef __AGG_PY_PATH_ITERATOR_H__ +#define __AGG_PY_PATH_ITERATOR_H__ + +#include "CXX/Objects.hxx" #define PY_ARRAY_TYPES_PREFIX NumPy #include "numpy/arrayobject.h" +#include "agg_path_storage.h" class PathIterator { PyArrayObject* vertices; @@ -66,3 +71,5 @@ agg::path_cmd_curve3, agg::path_cmd_curve4, agg::path_cmd_end_poly | agg::path_flags_close}; + +#endif // __AGG_PY_PATH_ITERATOR_H__ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |