Skip to content

Commit 0b1a2d4

Browse files
committed
Simplify projection-of-point-on-polyline in contour.py.
1 parent 8d2c16b commit 0b1a2d4

File tree

1 file changed

+27
-55
lines changed

1 file changed

+27
-55
lines changed

lib/matplotlib/contour.py

+27-55
Original file line numberDiff line numberDiff line change
@@ -598,31 +598,6 @@ def labels(self, inline, inline_spacing):
598598
paths[:] = additions
599599

600600

601-
def _find_closest_point_on_leg(p1, p2, p0):
602-
"""Find the closest point to p0 on line segment connecting p1 and p2."""
603-
604-
# handle degenerate case
605-
if np.all(p2 == p1):
606-
d = np.sum((p0 - p1)**2)
607-
return d, p1
608-
609-
d21 = p2 - p1
610-
d01 = p0 - p1
611-
612-
# project on to line segment to find closest point
613-
proj = np.dot(d01, d21) / np.dot(d21, d21)
614-
if proj < 0:
615-
proj = 0
616-
if proj > 1:
617-
proj = 1
618-
pc = p1 + proj * d21
619-
620-
# find squared distance
621-
d = np.sum((pc-p0)**2)
622-
623-
return d, pc
624-
625-
626601
def _is_closed_polygon(X):
627602
"""
628603
Return whether first and last object in a sequence are the same. These are
@@ -632,39 +607,36 @@ def _is_closed_polygon(X):
632607
return np.all(X[0] == X[-1])
633608

634609

635-
def _find_closest_point_on_path(lc, point):
610+
def _find_closest_point_on_path(xys, p):
636611
"""
637612
Parameters
638613
----------
639-
lc : coordinates of vertices
640-
point : coordinates of test point
614+
xys : (N, 2) array-like
615+
Coordinates of vertices.
616+
p : (float, float)
617+
Coordinates of point.
618+
619+
Returns
620+
-------
621+
d2min : float
622+
Minimum square distance of *p* to *xys*.
623+
proj : (float, float)
624+
Projection of *p* onto *xys*.
625+
imin : (int, int)
626+
Consecutive indices of vertices of segment in *xys* where *proj* is.
641627
"""
642-
643-
# find index of closest vertex for this segment
644-
ds = np.sum((lc - point[None, :])**2, 1)
645-
imin = np.argmin(ds)
646-
647-
dmin = np.inf
648-
xcmin = None
649-
legmin = (None, None)
650-
651-
closed = _is_closed_polygon(lc)
652-
653-
# build list of legs before and after this vertex
654-
legs = []
655-
if imin > 0 or closed:
656-
legs.append(((imin-1) % len(lc), imin))
657-
if imin < len(lc) - 1 or closed:
658-
legs.append((imin, (imin+1) % len(lc)))
659-
660-
for leg in legs:
661-
d, xc = _find_closest_point_on_leg(lc[leg[0]], lc[leg[1]], point)
662-
if d < dmin:
663-
dmin = d
664-
xcmin = xc
665-
legmin = leg
666-
667-
return (dmin, xcmin, legmin)
628+
if len(xys) == 1:
629+
return (((p - xys[0]) ** 2).sum(), xys[0], (0, 0))
630+
dxys = xys[1:] - xys[:-1] # Individual segment vectors.
631+
with np.errstate(invalid="ignore"): # 0/0 from zero-length segments.
632+
rel_projs = np.clip( # Proj. onto each segment in relative 0-1 coords.
633+
((p - xys[:-1]) * dxys).sum(axis=1)
634+
/ np.maximum((dxys ** 2).sum(axis=1), 1),
635+
0, 1)[:, None]
636+
projs = xys[:-1] + rel_projs * dxys # Projs. onto each segment, in (x, y).
637+
d2s = ((projs - p) ** 2).sum(axis=1) # Distances.
638+
imin = np.argmin(d2s)
639+
return (d2s[imin], projs[imin], (imin, imin+1))
668640

669641

670642
class ContourSet(cm.ScalarMappable, ContourLabeler):
@@ -1333,7 +1305,7 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
13331305
xmin, ymin : float
13341306
The point in the contour plot that is closest to ``(x, y)``.
13351307
d : float
1336-
The distance from ``(xmin, ymin)`` to ``(x, y)``.
1308+
The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
13371309
"""
13381310

13391311
# This function uses a method that is probably quite

0 commit comments

Comments
 (0)