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

Reply via email to