Revision: 4481
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4481&view=rev
Author: mdboom
Date: 2007-11-28 05:42:39 -0800 (Wed, 28 Nov 2007)
Log Message:
-----------
Major speed improvements for auto-placing of legends.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/legend.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/path.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/src/_path.cpp
Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py 2007-11-28 13:40:54 UTC
(rev 4480)
+++ branches/transforms/lib/matplotlib/legend.py 2007-11-28 13:42:39 UTC
(rev 4481)
@@ -36,33 +36,6 @@
from text import Text
from transforms import Affine2D, Bbox, BboxTransformTo
-def line_cuts_bbox(line, bbox):
- """ Return True if and only if line cuts bbox. """
- minx, miny, width, height = bbox.bounds
- maxx = minx + width
- maxy = miny + height
-
- n = len(line)
- if n == 0:
- return False
-
- if n == 1:
- return bbox.contains(line[0][0], line[0][1])
- p1 = line[0]
- for p2 in line[1:]:
- segment = (p1, p2)
- # See if the segment cuts any of the edges of bbox
- for edge in (((minx, miny), (minx, maxy)),
- ((minx, miny), (maxx, miny)),
- ((maxx, miny), (maxx, maxy)),
- ((minx, maxy), (maxx, maxy))):
- if segments_intersect(segment, edge):
- return True
- p1=p2
-
- return False
-
-
class Legend(Artist):
"""
Place a legend on the axes at location loc. Labels are a
@@ -344,11 +317,11 @@
for handle in ax.lines:
assert isinstance(handle, Line2D)
- data = handle.get_xydata()
+ path = handle.get_path()
trans = handle.get_transform()
- tdata = trans.transform(data)
- averts = inverse_transform.transform(tdata)
- lines.append(averts)
+ tpath = trans.transform_path(path)
+ apath = inverse_transform.transform_path(tpath)
+ lines.append(apath)
for handle in ax.patches:
assert isinstance(handle, Patch)
@@ -435,7 +408,7 @@
badness = legendBox.count_contains(verts)
badness += legendBox.count_overlaps(bboxes)
for line in lines:
- if line_cuts_bbox(line, legendBox):
+ if line.intersects_bbox(legendBox):
badness += 1
ox, oy = l-tx, b-ty
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-11-28 13:40:54 UTC (rev
4480)
+++ branches/transforms/lib/matplotlib/lines.py 2007-11-28 13:42:39 UTC (rev
4481)
@@ -285,6 +285,7 @@
self._xorig = npy.asarray([])
self._yorig = npy.asarray([])
+ self._invalid = True
self.set_data(xdata, ydata)
def contains(self, mouseevent):
@@ -353,7 +354,7 @@
def get_window_extent(self, renderer):
bbox = Bbox.unit()
- bbox.update_from_data_xy(self.get_transform().transform(self._xy),
+
bbox.update_from_data_xy(self.get_transform().transform(self.get_xydata()),
ignore=True)
# correct for marker size, if any
if self._marker is not None:
@@ -394,9 +395,10 @@
(y.shape != self._yorig.shape or npy.any(y != self._yorig)))):
self._xorig = x
self._yorig = y
- self.recache()
+ self._invalid = True
else:
- self._transformed_path._invalid =
self._transformed_path.INVALID_NON_AFFINE
+ if hasattr(self, "_transformed_path"):
+ self._transformed_path._invalid =
self._transformed_path.INVALID_NON_AFFINE
def recache(self):
#if self.axes is None: print 'recache no axes'
@@ -434,6 +436,7 @@
self._path = Path(self._xy)
self._transformed_path = TransformedPath(self._path,
self.get_transform())
+ self._invalid = False
def set_transform(self, t):
"""
@@ -442,7 +445,8 @@
ACCEPTS: a matplotlib.transforms.Transform instance
"""
Artist.set_transform(self, t)
- self._transformed_path = TransformedPath(self._path,
self.get_transform())
+ self._invalid = True
+ # self._transformed_path = TransformedPath(self._path,
self.get_transform())
def _is_sorted(self, x):
"return true if x is sorted"
@@ -450,6 +454,9 @@
return npy.alltrue(x[1:]-x[0:-1]>=0)
def draw(self, renderer):
+ if self._invalid:
+ self.recache()
+
renderer.open_group('line2d')
if not self._visible: return
@@ -531,6 +538,8 @@
"""
if orig:
return self._xorig
+ if self._invalid:
+ self.recache()
return self._x
def get_ydata(self, orig=True):
@@ -540,9 +549,21 @@
"""
if orig:
return self._yorig
+ if self._invalid:
+ self.recache()
return self._y
+ def get_path(self):
+ """
+ Return the Path object associated with this line.
+ """
+ if self._invalid:
+ self.recache()
+ return self._path
+
def get_xydata(self):
+ if self._invalid:
+ self.recache()
return self._xy
def set_antialiased(self, b):
Modified: branches/transforms/lib/matplotlib/path.py
===================================================================
--- branches/transforms/lib/matplotlib/path.py 2007-11-28 13:40:54 UTC (rev
4480)
+++ branches/transforms/lib/matplotlib/path.py 2007-11-28 13:42:39 UTC (rev
4481)
@@ -12,7 +12,7 @@
from matplotlib._path import point_in_path, get_path_extents, \
point_in_path_collection, get_path_collection_extents, \
- path_in_path
+ path_in_path, path_intersects_path
from matplotlib.cbook import simple_linear_interpolation
KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0
@@ -237,6 +237,22 @@
transform = Affine2D()
return Bbox.from_extents(*get_path_extents(self, transform))
+ def intersects_path(self, other):
+ """
+ Returns True if this path intersects another given path.
+ """
+ return path_intersects_path(self, other)
+
+ def intersects_bbox(self, bbox):
+ """
+ Returns True if this path intersects a given Bbox.
+ """
+ from transforms import BboxTransformTo
+ rectangle = self.unit_rectangle().transformed(
+ BboxTransformTo(bbox))
+ result = self.intersects_path(rectangle)
+ return result
+
def interpolated(self, steps):
"""
Returns a new path resampled to length N x steps.
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-11-28 13:40:54 UTC
(rev 4480)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-11-28 13:42:39 UTC
(rev 4481)
@@ -508,26 +508,8 @@
bboxes is a sequence of Bbox objects
"""
- ax1, ay1, ax2, ay2 = self._get_extents()
- if ax2 < ax1:
- ax2, ax1 = ax1, ax2
- if ay2 < ay1:
- ay2, ay1 = ay1, ay2
+ return count_bboxes_overlapping_bbox(self, bboxes)
- count = 0
- for bbox in bboxes:
- # bx1, by1, bx2, by2 = bbox._get_extents() ... inlined...
- bx1, by1, bx2, by2 = bbox.get_points().flatten()
- if bx2 < bx1:
- bx2, bx1 = bx1, bx2
- if by2 < by1:
- by2, by1 = by1, by2
- count += (not ((bx2 <= ax1) or
- (by2 <= ay1) or
- (bx1 >= ax2) or
- (by1 >= ay2)))
- return count
-
def expanded(self, sw, sh):
"""
Return a new Bbox which is this Bbox expanded around its
Modified: branches/transforms/src/_path.cpp
===================================================================
--- branches/transforms/src/_path.cpp 2007-11-28 13:40:54 UTC (rev 4480)
+++ branches/transforms/src/_path.cpp 2007-11-28 13:42:39 UTC (rev 4481)
@@ -36,13 +36,15 @@
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)");
+ "path_in_path(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)");
add_varargs_method("count_bboxes_overlapping_bbox",
&_path_module::count_bboxes_overlapping_bbox,
"count_bboxes_overlapping_bbox(bbox, bboxes)");
+ add_varargs_method("path_intersects_path",
&_path_module::path_intersects_path,
+ "path_intersects_path(p1, p2)");
initialize("Helper functions for paths");
}
@@ -60,6 +62,7 @@
Py::Object clip_path_to_rect(const Py::Tuple& args);
Py::Object affine_transform(const Py::Tuple& args);
Py::Object count_bboxes_overlapping_bbox(const Py::Tuple& args);
+ Py::Object path_intersects_path(const Py::Tuple& args);
};
//
@@ -673,7 +676,8 @@
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)
+ 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;
@@ -783,6 +787,70 @@
return Py::Int(count);
}
+bool segments_intersect(const double& x1, const double &y1,
+ const double& x2, const double &y2,
+ const double& x3, const double &y3,
+ const double& x4, const double &y4) {
+ double den = ((y4-y3) * (x2-x1)) - ((x4-x3)*(y2-y1));
+ if (den == 0.0)
+ return false;
+
+ double n1 = ((x4-x3) * (y1-y3)) - ((y4-y3)*(x1-x3));
+ double n2 = ((x2-x1) * (y1-y3)) - ((y2-y1)*(x1-x3));
+
+ double u1 = n1/den;
+ double u2 = n2/den;
+
+ return (u1 >= 0.0 && u1 <= 1.0 &&
+ u2 >= 0.0 && u2 <= 1.0);
+}
+
+bool path_intersects_path(PathIterator& p1, PathIterator& p2) {
+ typedef agg::conv_curve<PathIterator> curve_t;
+
+ if (p1.total_vertices() < 2 || p2.total_vertices() < 2)
+ return false;
+
+ curve_t c1(p1);
+ curve_t c2(p2);
+
+ double x11, y11, x12, y12;
+ double x21, y21, x22, y22;
+
+ c1.vertex(&x11, &y11);
+ while (c1.vertex(&x12, &y12) != agg::path_cmd_stop) {
+ c2.rewind(0);
+ c2.vertex(&x21, &y21);
+ while (c2.vertex(&x22, &y22) != agg::path_cmd_stop) {
+ if (segments_intersect(x11, y11, x12, y12, x21, y21, x22, y22))
+ return true;
+ x21 = x22; y21 = y22;
+ }
+ x11 = x12; y11 = y12;
+ }
+
+ return false;
+}
+
+Py::Object _path_module::path_intersects_path(const Py::Tuple& args) {
+ args.verify_length(2);
+
+ PathIterator p1(args[0]);
+ PathIterator p2(args[1]);
+
+ bool intersects = ::path_intersects_path(p1, p2);
+ if (!intersects) {
+ intersects = ::path_in_path(p1, agg::trans_affine(), p2,
agg::trans_affine());
+ if (!intersects) {
+ intersects = ::path_in_path(p2, agg::trans_affine(), p1,
agg::trans_affine());
+ if (!intersects) {
+ return Py::Int(0);
+ }
+ }
+ }
+ return Py::Int(1);
+}
+
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.
-------------------------------------------------------------------------
SF.Net email is sponsored by: The Future of Linux Business White Paper
from Novell. From the desktop to the data center, Linux is going
mainstream. Let it simplify your IT future.
http://altfarm.mediaplex.com/ad/ck/8857-50307-18918-4
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins