Revision: 3993 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3993&view=rev Author: mdboom Date: 2007-10-24 10:11:00 -0700 (Wed, 24 Oct 2007)
Log Message: ----------- Forgot to svn add these files in last commit. Added Paths: ----------- branches/transforms/src/_path.cpp branches/transforms/src/agg_py_transforms.h Added: branches/transforms/src/_path.cpp =================================================================== --- branches/transforms/src/_path.cpp (rev 0) +++ branches/transforms/src/_path.cpp 2007-10-24 17:11:00 UTC (rev 3993) @@ -0,0 +1,384 @@ +#include "agg_py_path_iterator.h" +#include "agg_py_transforms.h" + +#include "CXX/Extensions.hxx" + +#include "agg_conv_curve.h" +#include "agg_conv_stroke.h" +#include "agg_conv_transform.h" +#include "agg_path_storage.h" +#include "agg_trans_affine.h" + +// the extension module +class _path_module : public Py::ExtensionModule<_path_module> +{ +public: + _path_module() + : Py::ExtensionModule<_path_module>( "_path" ) + { + add_varargs_method("point_in_path", &_path_module::point_in_path, + "point_in_path(x, y, path, trans)"); + add_varargs_method("point_on_path", &_path_module::point_on_path, + "point_on_path(x, y, r, path, trans)"); + add_varargs_method("get_path_extents", &_path_module::get_path_extents, + "get_path_extents(path, trans)"); + add_varargs_method("get_path_collection_extents", &_path_module::get_path_collection_extents, + "get_path_collection_extents(trans, paths, transforms, offsets, offsetTrans)"); + add_varargs_method("point_in_path_collection", &_path_module::point_in_path_collection, + "point_in_path_collection(x, y, r, trans, paths, transforms, offsets, offsetTrans, filled)"); + + initialize("Helper functions for paths"); + } + + virtual ~_path_module() {} + +private: + + 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); +}; + +// +// The following function 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, [EMAIL PROTECTED] +// +// 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 _path_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 _path_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 _path_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 _path_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 _path_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; +} + +extern "C" +DL_EXPORT(void) + init_path(void) +{ + import_array(); + + static _path_module* _path = NULL; + _path = new _path_module; +}; Added: branches/transforms/src/agg_py_transforms.h =================================================================== --- branches/transforms/src/agg_py_transforms.h (rev 0) +++ branches/transforms/src/agg_py_transforms.h 2007-10-24 17:11:00 UTC (rev 3993) @@ -0,0 +1,58 @@ +#ifndef __AGG_PY_TRANSFORMS_H__ +#define __AGG_PY_TRANSFORMS_H__ + +#define PY_ARRAY_TYPES_PREFIX NumPy +#include "numpy/arrayobject.h" + +#include "CXX/Objects.hxx" +#include "agg_trans_affine.h" + + +/** 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 std::exception(); + matrix = (PyArrayObject*) PyArray_FromObject(obj.ptr(), PyArray_DOUBLE, 2, 2); + if (!matrix) + throw std::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 std::exception(); + } catch (...) { + if (errors) { + Py_XDECREF(matrix); + throw Py::TypeError("Invalid affine transformation matrix"); + } + } + + Py_XDECREF(matrix); + return agg::trans_affine(); +} + +#endif // __AGG_PY_TRANSFORMS_H__ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ Matplotlib-checkins mailing list Matplotlib-checkins@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins