Implement Path.intersects_bbox in C++ to speed up legend positioning. · matplotlib/matplotlib@c814beb · GitHub
Skip to content

Commit c814beb

Browse files
committed
Implement Path.intersects_bbox in C++ to speed up legend positioning.
1 parent ccc8f16 commit c814beb

5 files changed

Lines changed: 1810 additions & 7 deletions

File tree

Lines changed: 21 additions & 0 deletions

lib/matplotlib/path.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -558,14 +558,13 @@ def intersects_bbox(self, bbox, filled=True):
558558
:class:`~matplotlib.transforms.Bbox`.
559559
560560
*filled*, when True, treats the path as if it was filled.
561-
That is, if one path completely encloses the other,
562-
:meth:`intersects_path` will return True.
561+
That is, if the path completely encloses the bounding box,
562+
:meth:`intersects_bbox` will return True.
563+
564+
The bounding box is always considered filled.
563565
"""
564-
from .transforms import BboxTransformTo
565-
rectangle = self.unit_rectangle().transformed(
566-
BboxTransformTo(bbox))
567-
result = self.intersects_path(rectangle, filled)
568-
return result
566+
return _path.path_intersects_rectangle(self,
567+
bbox.x0, bbox.y0, bbox.x1, bbox.y1, filled)
569568

570569
def interpolated(self, steps):
571570
"""

src/_path.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,66 @@ bool path_intersects_path(PathIterator1 &p1, PathIterator2 &p2)
872872
return false;
873873
}
874874

875+
// returns whether the segment from (x1,y1) to (x2,y2)
876+
// intersects the rectangle centered at (cx,cy) with size (w,h)
877+
// see doc/segment_intersects_rectangle.svg for a more detailed explanation
878+
inline bool segment_intersects_rectangle(double x1, double y1,
879+
double x2, double y2,
880+
double cx, double cy,
881+
double w, double h)
882+
{
883+
return fabs(x1 + x2 - 2.0 * cx) < fabs(x1 - x2) + w &&
884+
fabs(y1 + y2 - 2.0 * cy) < fabs(y1 - y2) + h &&
885+
2.0 * fabs((x1 - cx) * (y1 - y2) - (y1 - cy) * (x1 - x2)) <
886+
w * fabs(y1 - y2) + h * fabs(x1 - x2);
887+
}
888+
889+
template <class PathIterator>
890+
bool path_intersects_rectangle(PathIterator &path,
891+
double rect_x1, double rect_y1,
892+
double rect_x2, double rect_y2,
893+
bool filled)
894+
{
895+
typedef PathNanRemover<py::PathIterator> no_nans_t;
896+
typedef agg::conv_curve<no_nans_t> curve_t;
897+
898+
if (path.total_vertices() == 0) {
899+
return false;
900+
}
901+
902+
no_nans_t no_nans(path, true, path.has_curves());
903+
curve_t curve(no_nans);
904+
905+
double cx = (rect_x1 + rect_x2) * 0.5, cy = (rect_y1 + rect_y2) * 0.5;
906+
double w = fabs(rect_x1 - rect_x2), h = fabs(rect_y1 - rect_y2);
907+
double xmin = std::min(rect_x1, rect_x2), xmax = std::max(rect_x1, rect_x2);
908+
double ymin = std::min(rect_x1, rect_x2), ymax = std::max(rect_x1, rect_x2);
909+
910+
double x1, y1, x2, y2;
911+
912+
curve.vertex(&x1, &y1);
913+
if (2.0 * fabs(x1 - cx) <= w && 2.0 * fabs(y1 - cy) <= h) {
914+
return true;
915+
}
916+
917+
while (curve.vertex(&x2, &y2) != agg::path_cmd_stop) {
918+
if (segment_intersects_rectangle(x1, y1, x2, y2, cx, cy, w, h)) {
919+
return true;
920+
}
921+
x1 = x2;
922+
y1 = y2;
923+
}
924+
925+
if (filled) {
926+
agg::trans_affine trans;
927+
if (point_in_path(cx, cy, 0.0, path, trans)) {
928+
return true;
929+
}
930+
}
931+
932+
return false;
933+
}
934+
875935
template <class PathIterator>
876936
void convert_path_to_polygons(PathIterator &path,
877937
agg::trans_affine &trans,

src/_path_wrapper.cpp

Lines changed: 34 additions & 0 deletions

0 commit comments

Comments
 (0)