Skip to content

Commit 4ad0765

Browse files
committed
Add a "radius" to point_in_path to prevent floating-point rounding error from making annotations disappear.
1 parent 2656639 commit 4ad0765

File tree

5 files changed

+26
-17
lines changed

5 files changed

+26
-17
lines changed

lib/matplotlib/axes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3001,7 +3001,7 @@ def contains_point(self, point):
30013001
required.
30023002
30033003
"""
3004-
return self.patch.contains_point(point)
3004+
return self.patch.contains_point(point, radius=1.0)
30053005

30063006
def pick(self, *args):
30073007
"""

lib/matplotlib/patches.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def get_verts(self):
112112
return polygons[0]
113113
return []
114114

115-
def contains(self, mouseevent):
115+
def contains(self, mouseevent, radius=None):
116116
"""Test whether the mouse event occurred in the patch.
117117
118118
Returns T/F, {}
@@ -122,18 +122,20 @@ def contains(self, mouseevent):
122122
# algebraic solution to hit-testing should override this
123123
# method.
124124
if callable(self._contains): return self._contains(self,mouseevent)
125-
125+
if radius is None:
126+
radius = self.get_linewidth()
126127
inside = self.get_path().contains_point(
127-
(mouseevent.x, mouseevent.y), self.get_transform())
128+
(mouseevent.x, mouseevent.y), self.get_transform(), radius)
128129
return inside, {}
129130

130-
def contains_point(self, point):
131+
def contains_point(self, point, radius=None):
131132
"""
132133
Returns *True* if the given point is inside the path
133134
(transformed with its transform attribute).
134135
"""
135-
return self.get_path().contains_point(point,
136-
self.get_transform())
136+
if radius is None:
137+
radius = self.get_linewidth()
138+
return self.get_path().contains_point(point, self.get_transform(), radius)
137139

138140
def update_from(self, other):
139141
"""

lib/matplotlib/path.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def transformed(self, transform):
265265
return Path(transform.transform(self.vertices), self.codes,
266266
self._interpolation_steps)
267267

268-
def contains_point(self, point, transform=None):
268+
def contains_point(self, point, transform=None, radius=0.0):
269269
"""
270270
Returns *True* if the path contains the given point.
271271
@@ -274,7 +274,8 @@ def contains_point(self, point, transform=None):
274274
"""
275275
if transform is not None:
276276
transform = transform.frozen()
277-
return point_in_path(point[0], point[1], self, transform)
277+
result = point_in_path(point[0], point[1], radius, self, transform)
278+
return result
278279

279280
def contains_path(self, path, transform=None):
280281
"""

setupext.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,7 @@ def build_path(ext_modules, packages):
11921192
if BUILT_PATH: return # only build it if you you haven't already
11931193

11941194
agg = (
1195+
'agg_vcgen_contour.cpp',
11951196
'agg_curves.cpp',
11961197
'agg_bezier_arc.cpp',
11971198
'agg_trans_affine.cpp',

src/_path.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "CXX/Extensions.hxx"
1111

12+
#include "agg_conv_contour.h"
1213
#include "agg_conv_curve.h"
1314
#include "agg_conv_stroke.h"
1415
#include "agg_conv_transform.h"
@@ -225,12 +226,13 @@ point_in_path_impl(const double tx, const double ty, T& path)
225226
}
226227

227228
inline bool
228-
point_in_path(double x, double y, PathIterator& path,
229+
point_in_path(double x, double y, double r, PathIterator& path,
229230
const agg::trans_affine& trans)
230231
{
231232
typedef agg::conv_transform<PathIterator> transformed_path_t;
232233
typedef PathNanRemover<transformed_path_t> no_nans_t;
233234
typedef agg::conv_curve<no_nans_t> curve_t;
235+
typedef agg::conv_contour<curve_t> contour_t;
234236

235237
if (path.total_vertices() < 3)
236238
{
@@ -240,7 +242,9 @@ point_in_path(double x, double y, PathIterator& path,
240242
transformed_path_t trans_path(path, trans);
241243
no_nans_t no_nans_path(trans_path, true, path.has_curves());
242244
curve_t curved_path(no_nans_path);
243-
return point_in_path_impl(x, y, curved_path);
245+
contour_t contoured_path(curved_path);
246+
contoured_path.width(fabs(r));
247+
return point_in_path_impl(x, y, contoured_path);
244248
}
245249

246250
inline bool
@@ -263,14 +267,15 @@ point_on_path(double x, double y, double r, PathIterator& path,
263267
Py::Object
264268
_path_module::point_in_path(const Py::Tuple& args)
265269
{
266-
args.verify_length(4);
270+
args.verify_length(5);
267271

268272
double x = Py::Float(args[0]);
269273
double y = Py::Float(args[1]);
270-
PathIterator path(args[2]);
271-
agg::trans_affine trans = py_to_agg_transformation_matrix(args[3].ptr(), false);
274+
double r = Py::Float(args[2]);
275+
PathIterator path(args[3]);
276+
agg::trans_affine trans = py_to_agg_transformation_matrix(args[4].ptr(), false);
272277

273-
if (::point_in_path(x, y, path, trans))
278+
if (::point_in_path(x, y, r, path, trans))
274279
{
275280
return Py::Int(1);
276281
}
@@ -664,7 +669,7 @@ _path_module::point_in_path_collection(const Py::Tuple& args)
664669

665670
if (filled)
666671
{
667-
if (::point_in_path(x, y, path, trans))
672+
if (::point_in_path(x, y, radius, path, trans))
668673
result.append(Py::Int((int)i));
669674
}
670675
else
@@ -696,7 +701,7 @@ path_in_path(PathIterator& a, const agg::trans_affine& atrans,
696701
b_curved.rewind(0);
697702
while (b_curved.vertex(&x, &y) != agg::path_cmd_stop)
698703
{
699-
if (!::point_in_path(x, y, a, atrans))
704+
if (!::point_in_path(x, y, 0.0, a, atrans))
700705
return false;
701706
}
702707

0 commit comments

Comments
 (0)