Revision: 4286
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4286&view=rev
Author: mdboom
Date: 2007-11-14 10:44:39 -0800 (Wed, 14 Nov 2007)
Log Message:
-----------
New path-related utilities (used for an aborted attempt at fixing
contouring -- may be useful in other contexts in the future).
Modified Paths:
--------------
branches/transforms/lib/matplotlib/patches.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/src/_path.cpp
Modified: branches/transforms/lib/matplotlib/patches.py
===================================================================
--- branches/transforms/lib/matplotlib/patches.py 2007-11-14 18:43:35 UTC
(rev 4285)
+++ branches/transforms/lib/matplotlib/patches.py 2007-11-14 18:44:39 UTC
(rev 4286)
@@ -152,7 +152,7 @@
ACCEPTS: any matplotlib color
"""
self._facecolor = color
-
+
def set_linewidth(self, w):
"""
Set the patch linewidth in points
@@ -522,6 +522,28 @@
def get_patch_transform(self):
return self._poly_transform
+class PathPatch(Patch):
+ """
+ A general polycurve path patch.
+ """
+ def __str__(self):
+ return "Poly((%g, %g) ...)" % tuple(self._path.vertices[0])
+
+ def __init__(self, path, **kwargs):
+ """
+ path is a Path object
+
+ Valid kwargs are:
+ %(Patch)s
+ See Patch documentation for additional kwargs
+ """
+ Patch.__init__(self, **kwargs)
+ self._path = path
+ __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+
+ def get_path(self):
+ return self._path
+
class Polygon(Patch):
"""
A general polygon patch.
@@ -549,7 +571,7 @@
def _set_xy(self, vertices):
self._path = Path(vertices)
xy = property(_get_xy, _set_xy)
-
+
class Wedge(Patch):
def __str__(self):
return "Wedge(%g,%g)"%self.xy[0]
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-11-14 18:43:35 UTC (rev
4285)
+++ branches/transforms/lib/matplotlib/path.py 2007-11-14 18:44:39 UTC (rev
4286)
@@ -11,8 +11,8 @@
from matplotlib.numerix import npyma as ma
from matplotlib._path import point_in_path, get_path_extents, \
- point_in_path_collection
-import matplotlib._path as _path
+ point_in_path_collection, get_path_collection_extents, \
+ path_in_path
from matplotlib.cbook import simple_linear_interpolation
KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0
@@ -128,6 +128,30 @@
self.codes = codes
self.vertices = vertices
+ [EMAIL PROTECTED]
+ def make_compound_path(*args):
+ """
+ Make a compound path from a list of Path objects. Only
+ polygons (not curves) are supported.
+ """
+ for p in args:
+ assert p.codes is None
+
+ lengths = [len(x) for x in args]
+ total_length = sum(lengths)
+
+ vertices = npy.vstack([x.vertices for x in args])
+ vertices.reshape((total_length, 2))
+
+ codes = Path.LINETO * npy.ones(total_length)
+ i = 0
+ for length in lengths:
+ codes[i] = Path.MOVETO
+ i += length
+
+ return Path(vertices, codes)
+ make_compound_path = staticmethod(make_compound_path)
+
def __repr__(self):
return "Path(%s, %s)" % (self.vertices, self.codes)
@@ -186,9 +210,18 @@
"""
if transform is None:
from transforms import IdentityTransform
- transform = IdentityTransform
+ transform = IdentityTransform()
return point_in_path(point[0], point[1], self, transform.frozen())
+ def contains_path(self, path, transform=None):
+ """
+ Returns True if this path completely contains the given path.
+ """
+ if transform is None:
+ from transforms import IdentityTransform
+ transform = IdentityTransform()
+ return path_in_path(self, IdentityTransform(), path, transform)
+
def get_extents(self, transform=None):
"""
Returns the extents (xmin, ymin, xmax, ymax) of the path.
@@ -408,8 +441,9 @@
return cls.arc(theta1, theta2, True)
wedge = classmethod(wedge)
+_get_path_collection_extents = get_path_collection_extents
def get_path_collection_extents(*args):
from transforms import Bbox
if len(args[1]) == 0:
raise ValueError("No paths provided")
- return Bbox.from_extents(*_path.get_path_collection_extents(*args))
+ return Bbox.from_extents(*_get_path_collection_extents(*args))
Modified: branches/transforms/src/_path.cpp
===================================================================
--- branches/transforms/src/_path.cpp 2007-11-14 18:43:35 UTC (rev 4285)
+++ branches/transforms/src/_path.cpp 2007-11-14 18:44:39 UTC (rev 4286)
@@ -9,6 +9,8 @@
#include "agg_path_storage.h"
#include "agg_trans_affine.h"
+// MGDTODO: Un-CXX-ify this module
+
// the extension module
class _path_module : public Py::ExtensionModule<_path_module>
{
@@ -26,6 +28,10 @@
"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)");
+ add_varargs_method("path_in_path", &_path_module::path_in_path,
+ "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)");
initialize("Helper functions for paths");
}
@@ -39,6 +45,8 @@
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);
+ Py::Object path_in_path(const Py::Tuple& args);
+ Py::Object clip_path_to_rect(const Py::Tuple& args);
};
//
@@ -85,11 +93,14 @@
double x, y;
path.rewind(0);
- unsigned code = path.vertex(&x, &y);
- if (code == agg::path_cmd_stop)
- return false;
+ inside_flag = 0;
+
+ unsigned code = 0;
while (true) {
+ if (code != agg::path_cmd_move_to)
+ code = path.vertex(&x, &y);
+
sx = vtx0 = x;
sy = vty0 = y;
@@ -104,11 +115,11 @@
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) {
+ if (code == agg::path_cmd_stop || (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.
@@ -146,26 +157,37 @@
break;
}
+ yflag1 = (vty1 >= ty);
+ if (yflag0 != yflag1) {
+ if ( ((vty1-ty) * (vtx0-vtx1) >=
+ (vtx1-tx) * (vty0-vty1)) == yflag1 ) {
+ inside_flag ^= 1;
+ }
+ }
+
if (inside_flag != 0)
return true;
if (code == agg::path_cmd_stop)
- return false;
+ break;
}
- return false;
+ return (inside_flag != 0);
}
-bool point_in_path(double x, double y, PathIterator& path, agg::trans_affine&
trans) {
+inline bool point_in_path(double x, double y, PathIterator& path, const
agg::trans_affine& trans) {
typedef agg::conv_transform<PathIterator> transformed_path_t;
typedef agg::conv_curve<transformed_path_t> curve_t;
+ if (path.total_vertices() < 3)
+ return false;
+
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) {
+inline bool point_on_path(double x, double y, double r, PathIterator& path,
const 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;
@@ -204,7 +226,7 @@
return Py::Int(0);
}
-void get_path_extents(PathIterator& path, agg::trans_affine& trans,
+void get_path_extents(PathIterator& path, const 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;
@@ -393,6 +415,248 @@
return result;
}
+bool path_in_path(PathIterator& a, const agg::trans_affine& atrans,
+ PathIterator& b, const agg::trans_affine& btrans) {
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef agg::conv_curve<transformed_path_t> curve_t;
+
+ if (a.total_vertices() < 3)
+ return false;
+
+ transformed_path_t b_path_trans(b, btrans);
+ curve_t b_curved(b_path_trans);
+
+ double x, y;
+ b_curved.rewind(0);
+ while (b_curved.vertex(&x, &y) != agg::path_cmd_stop) {
+ if (!::point_in_path(x, y, a, atrans))
+ return false;
+ }
+
+ return true;
+}
+
+Py::Object _path_module::path_in_path(const Py::Tuple& args) {
+ args.verify_length(4);
+
+ PathIterator a(args[0]);
+ agg::trans_affine atrans = py_to_agg_transformation_matrix(args[1]);
+ PathIterator b(args[2]);
+ agg::trans_affine btrans = py_to_agg_transformation_matrix(args[3]);
+
+ return Py::Int(::path_in_path(a, atrans, b, btrans));
+}
+
+/** The clip_path_to_rect code here is a clean-room implementation of the
+ Sutherland-Hodgman clipping algorithm described here:
+
+ http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
+*/
+
+typedef std::vector<std::pair<double, double> > Polygon;
+
+namespace clip_to_rect_filters {
+ /* There are four different passes needed to create/remove vertices
+ (one for each side of the rectangle). The differences between those
+ passes are encapsulated in these functor classes.
+ */
+ struct bisectx {
+ double m_x;
+
+ bisectx(double x) : m_x(x) {}
+
+ void bisect(double sx, double sy, double px, double py, double* bx,
double* by) const {
+ *bx = m_x;
+ double dx = px - sx;
+ double dy = py - sy;
+ *by = sy + dy * ((m_x - sx) / dx);
+ }
+ };
+
+ struct xlt : public bisectx {
+ xlt(double x) : bisectx(x) {}
+
+ bool operator()(double x, double y) const {
+ return x <= m_x;
+ }
+ };
+
+ struct xgt : public bisectx {
+ xgt(double x) : bisectx(x) {}
+
+ bool operator()(double x, double y) const {
+ return x >= m_x;
+ }
+ };
+
+ struct bisecty {
+ double m_y;
+
+ bisecty(double y) : m_y(y) {}
+
+ void bisect(double sx, double sy, double px, double py, double* bx,
double* by) const {
+ *by = m_y;
+ double dx = px - sx;
+ double dy = py - sy;
+ *bx = sx + dx * ((m_y - sy) / dy);
+ }
+ };
+
+ struct ylt : public bisecty {
+ ylt(double y) : bisecty(y) {}
+
+ bool operator()(double x, double y) const {
+ return y <= m_y;
+ }
+ };
+
+ struct ygt : public bisecty {
+ ygt(double y) : bisecty(y) {}
+
+ bool operator()(double x, double y) const {
+ return y >= m_y;
+ }
+ };
+}
+
+template<class Filter>
+void clip_to_rect_one_step(const Polygon& polygon, Polygon& result, const
Filter& filter) {
+ double sx, sy, px, py, bx, by;
+ bool sinside, pinside;
+ result.clear();
+
+ if (polygon.size() == 0)
+ return;
+
+ sx = polygon.back().first;
+ sy = polygon.back().second;
+ for (Polygon::const_iterator i = polygon.begin(); i != polygon.end(); ++i) {
+ px = i->first;
+ py = i->second;
+
+ sinside = filter(sx, sy);
+ pinside = filter(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));
+ }
+ }
+
+ sx = px; sy = py;
+ }
+}
+
+void clip_to_rect(PathIterator& path,
+ double x0, double y0, double x1, double y1,
+ bool inside, std::vector<Polygon>& results) {
+ double xmin, ymin, xmax, ymax;
+ if (x0 < x1) {
+ xmin = x0; xmax = x1;
+ } else {
+ xmin = x1; xmax = x0;
+ }
+
+ if (y0 < y1) {
+ ymin = y0; ymax = y1;
+ } else {
+ ymin = y1; ymax = y0;
+ }
+
+ if (!inside) {
+ std::swap(xmin, xmax);
+ std::swap(ymin, ymax);
+ }
+
+ Polygon polygon1, polygon2;
+ double x, y;
+ unsigned code = 0;
+ path.rewind(0);
+
+ while (true) {
+ polygon1.clear();
+ while (true) {
+ if (code == agg::path_cmd_move_to)
+ polygon1.push_back(std::make_pair(x, y));
+
+ code = path.vertex(&x, &y);
+
+ if (code == agg::path_cmd_stop)
+ break;
+
+ if (code != agg::path_cmd_move_to)
+ polygon1.push_back(std::make_pair(x, y));
+
+ 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));
+ clip_to_rect_one_step(polygon2, polygon1, clip_to_rect_filters::xgt(xmin));
+ 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));
+
+ if (polygon1.size())
+ results.push_back(polygon1);
+
+ if (code == agg::path_cmd_stop)
+ break;
+ }
+}
+
+Py::Object _path_module::clip_path_to_rect(const Py::Tuple &args) {
+ args.verify_length(3);
+
+ PathIterator path(args[0]);
+ Py::Object bbox_obj = args[1];
+ bool inside = Py::Int(args[2]);
+
+ double x0, y0, x1, y1;
+ if (!py_convert_bbox(bbox_obj.ptr(), x0, y0, x1, y1))
+ throw Py::TypeError("Argument 2 to clip_to_rect must be a Bbox object.");
+
+ std::vector<Polygon> results;
+
+ ::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;
+ }
+ // MGDTODO: Error check
+ PyList_SetItem(py_results, p - results.begin(), (PyObject *)pyarray);
+ }
+
+ return Py::Object(py_results, true);
+}
+
+struct XY {
+ double x;
+ double y;
+};
+
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.
-------------------------------------------------------------------------
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins