Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fe8b98

Browse files
committed
refactor Bezier so it doesn't depend on Path
1 parent db55918 commit 6fe8b98

File tree

5 files changed

+116
-96
lines changed

5 files changed

+116
-96
lines changed

doc/api/next_api_changes/deprecations.rst

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,10 @@ also be accessible as ``toolbar.parent()``.
374374

375375
Path helpers in :mod:`.bezier`
376376
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
377-
378-
``bezier.make_path_regular`` is deprecated. Use ``Path.cleaned()`` (or
379-
``Path.cleaned(curves=True)``, etc.) instead (but note that these methods add a
380-
``STOP`` code at the end of the path).
381-
382-
``bezier.concatenate_paths`` is deprecated. Use ``Path.make_compound_path()``
383-
instead.
377+
- ``bezier.make_path_regular`` is deprecated. Use ``Path.cleaned()`` (or
378+
``Path.cleaned(curves=True)``, etc.) instead (but note that these methods add
379+
a ``STOP`` code at the end of the path).
380+
- ``bezier.concatenate_paths`` is deprecated. Use ``Path.make_compound_path()``
381+
instead.
382+
- ``bezier.split_path_inout`` (use ``Path.split_path_inout`` instead)
383+
- ``bezier.inside_circle()`` (no replacement)

doc/api/next_api_changes/removals.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Classes, methods and attributes
103103

104104
- ``image.BboxImage.interp_at_native`` property (no replacement)
105105
- ``lines.Line2D.verticalOffset`` property (no replacement)
106-
- ``bezier.find_r_to_boundary_of_closedpath()`` (no relacement)
106+
- ``bezier.find_r_to_boundary_of_closedpath()`` (no replacement)
107107

108108
- ``quiver.Quiver.color()`` (use ``Quiver.get_facecolor()`` instead)
109109
- ``quiver.Quiver.keyvec`` property (no replacement)

lib/matplotlib/bezier.py

Lines changed: 17 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import numpy as np
88

99
import matplotlib.cbook as cbook
10-
from matplotlib.path import Path
1110

1211

1312
class NonIntersectingPathException(ValueError):
1413
pass
1514

15+
1616
# some functions
1717

1818

@@ -68,6 +68,15 @@ def get_normal_points(cx, cy, cos_t, sin_t, length):
6868
return x1, y1, x2, y2
6969

7070

71+
@cbook.deprecated("3.3", alternative="Path.split_path_inout()")
72+
def split_path_inout(path, inside, tolerance=0.01, reorder_inout=False):
73+
"""
74+
Divide a path into two segments at the point where ``inside(x, y)``
75+
becomes False.
76+
"""
77+
return path.split_path_inout(inside, tolerance, reorder_inout)
78+
79+
7180
# BEZIER routines
7281

7382
# subdividing bezier curve
@@ -222,69 +231,7 @@ def split_bezier_intersecting_with_closedpath(
222231
return _left, _right
223232

224233

225-
# matplotlib specific
226-
227-
228-
def split_path_inout(path, inside, tolerance=0.01, reorder_inout=False):
229-
"""
230-
Divide a path into two segments at the point where ``inside(x, y)`` becomes
231-
False.
232-
"""
233-
path_iter = path.iter_segments()
234-
235-
ctl_points, command = next(path_iter)
236-
begin_inside = inside(ctl_points[-2:]) # true if begin point is inside
237-
238-
ctl_points_old = ctl_points
239-
240-
iold = 0
241-
i = 1
242-
243-
for ctl_points, command in path_iter:
244-
iold = i
245-
i += len(ctl_points) // 2
246-
if inside(ctl_points[-2:]) != begin_inside:
247-
bezier_path = np.concatenate([ctl_points_old[-2:], ctl_points])
248-
break
249-
ctl_points_old = ctl_points
250-
else:
251-
raise ValueError("The path does not intersect with the patch")
252-
253-
bp = bezier_path.reshape((-1, 2))
254-
left, right = split_bezier_intersecting_with_closedpath(
255-
bp, inside, tolerance)
256-
if len(left) == 2:
257-
codes_left = [Path.LINETO]
258-
codes_right = [Path.MOVETO, Path.LINETO]
259-
elif len(left) == 3:
260-
codes_left = [Path.CURVE3, Path.CURVE3]
261-
codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3]
262-
elif len(left) == 4:
263-
codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4]
264-
codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
265-
else:
266-
raise AssertionError("This should never be reached")
267-
268-
verts_left = left[1:]
269-
verts_right = right[:]
270-
271-
if path.codes is None:
272-
path_in = Path(np.concatenate([path.vertices[:i], verts_left]))
273-
path_out = Path(np.concatenate([verts_right, path.vertices[i:]]))
274-
275-
else:
276-
path_in = Path(np.concatenate([path.vertices[:iold], verts_left]),
277-
np.concatenate([path.codes[:iold], codes_left]))
278-
279-
path_out = Path(np.concatenate([verts_right, path.vertices[i:]]),
280-
np.concatenate([codes_right, path.codes[i:]]))
281-
282-
if reorder_inout and not begin_inside:
283-
path_in, path_out = path_out, path_in
284-
285-
return path_in, path_out
286-
287-
234+
@cbook.deprecated("3.3")
288235
def inside_circle(cx, cy, r):
289236
"""
290237
Return a function that checks whether a point is in a circle with center
@@ -294,16 +241,13 @@ def inside_circle(cx, cy, r):
294241
295242
f(xy: Tuple[float, float]) -> bool
296243
"""
297-
r2 = r ** 2
298-
299-
def _f(xy):
300-
x, y = xy
301-
return (x - cx) ** 2 + (y - cy) ** 2 < r2
302-
return _f
244+
from .patches import _inside_circle
245+
return _inside_circle(cx, cy, r)
303246

304247

305248
# quadratic Bezier lines
306249

250+
307251
def get_cos_sin(x0, y0, x1, y1):
308252
dx, dy = x1 - x0, y1 - y0
309253
d = (dx * dx + dy * dy) ** .5
@@ -486,6 +430,7 @@ def make_path_regular(p):
486430
with ``codes`` set to (MOVETO, LINETO, LINETO, ..., LINETO); otherwise
487431
return *p* itself.
488432
"""
433+
from .path import Path
489434
c = p.codes
490435
if c is None:
491436
c = np.full(len(p.vertices), Path.LINETO, dtype=Path.code_type)
@@ -498,6 +443,5 @@ def make_path_regular(p):
498443
@cbook.deprecated("3.3", alternative="Path.make_compound_path()")
499444
def concatenate_paths(paths):
500445
"""Concatenate a list of paths into a single path."""
501-
vertices = np.concatenate([p.vertices for p in paths])
502-
codes = np.concatenate([make_path_regular(p).codes for p in paths])
503-
return Path(vertices, codes)
446+
from .path import Path
447+
return Path.make_compound_path(*paths)

lib/matplotlib/patches.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,28 @@
1010
import matplotlib as mpl
1111
from . import artist, cbook, colors, docstring, lines as mlines, transforms
1212
from .bezier import (
13-
NonIntersectingPathException, get_cos_sin, get_intersection,
14-
get_parallels, inside_circle, make_wedged_bezier2,
15-
split_bezier_intersecting_with_closedpath, split_path_inout)
13+
NonIntersectingPathException, get_cos_sin, get_intersection, get_parallels,
14+
make_wedged_bezier2, split_bezier_intersecting_with_closedpath)
1615
from .path import Path
1716

1817

18+
def _inside_circle(cx, cy, r):
19+
"""
20+
Return a function that checks whether a point is in a circle with center
21+
(*cx*, *cy*) and radius *r*.
22+
23+
The returned function has the signature::
24+
25+
f(xy: Tuple[float, float]) -> bool
26+
"""
27+
r2 = r ** 2
28+
29+
def _f(xy):
30+
x, y = xy
31+
return (x - cx) ** 2 + (y - cy) ** 2 < r2
32+
return _f
33+
34+
1935
@cbook._define_aliases({
2036
"antialiased": ["aa"],
2137
"edgecolor": ["ec"],
@@ -2428,7 +2444,7 @@ def insideA(xy_display):
24282444
return patchA.contains(xy_event)[0]
24292445

24302446
try:
2431-
left, right = split_path_inout(path, insideA)
2447+
left, right = path.split_path_inout(insideA)
24322448
except ValueError:
24332449
right = path
24342450

@@ -2440,7 +2456,7 @@ def insideB(xy_display):
24402456
return patchB.contains(xy_event)[0]
24412457

24422458
try:
2443-
left, right = split_path_inout(path, insideB)
2459+
left, right = path.split_path_inout(insideB)
24442460
except ValueError:
24452461
left = path
24462462

@@ -2453,15 +2469,15 @@ def _shrink(self, path, shrinkA, shrinkB):
24532469
Shrink the path by fixed size (in points) with shrinkA and shrinkB.
24542470
"""
24552471
if shrinkA:
2456-
insideA = inside_circle(*path.vertices[0], shrinkA)
2472+
insideA = _inside_circle(*path.vertices[0], shrinkA)
24572473
try:
2458-
left, path = split_path_inout(path, insideA)
2474+
left, path = path.split_path_inout(insideA)
24592475
except ValueError:
24602476
pass
24612477
if shrinkB:
2462-
insideB = inside_circle(*path.vertices[-1], shrinkB)
2478+
insideB = _inside_circle(*path.vertices[-1], shrinkB)
24632479
try:
2464-
path, right = split_path_inout(path, insideB)
2480+
path, right = path.split_path_inout(insideB)
24652481
except ValueError:
24662482
pass
24672483
return path
@@ -2886,7 +2902,6 @@ def __call__(self, path, mutation_size, linewidth,
28862902
The __call__ method is a thin wrapper around the transmute method
28872903
and takes care of the aspect ratio.
28882904
"""
2889-
28902905
if aspect_ratio is not None:
28912906
# Squeeze the given height by the aspect_ratio
28922907
vertices = path.vertices / [1, aspect_ratio]
@@ -3351,7 +3366,7 @@ def transmute(self, path, mutation_size, linewidth):
33513366

33523367
# divide the path into a head and a tail
33533368
head_length = self.head_length * mutation_size
3354-
in_f = inside_circle(x2, y2, head_length)
3369+
in_f = _inside_circle(x2, y2, head_length)
33553370
arrow_path = [(x0, y0), (x1, y1), (x2, y2)]
33563371

33573372
try:
@@ -3434,7 +3449,7 @@ def transmute(self, path, mutation_size, linewidth):
34343449
arrow_path = [(x0, y0), (x1, y1), (x2, y2)]
34353450

34363451
# path for head
3437-
in_f = inside_circle(x2, y2, head_length)
3452+
in_f = _inside_circle(x2, y2, head_length)
34383453
try:
34393454
path_out, path_in = split_bezier_intersecting_with_closedpath(
34403455
arrow_path, in_f, tolerance=0.01)
@@ -3449,7 +3464,7 @@ def transmute(self, path, mutation_size, linewidth):
34493464
path_head = path_in
34503465

34513466
# path for head
3452-
in_f = inside_circle(x2, y2, head_length * .8)
3467+
in_f = _inside_circle(x2, y2, head_length * .8)
34533468
path_out, path_in = split_bezier_intersecting_with_closedpath(
34543469
arrow_path, in_f, tolerance=0.01)
34553470
path_tail = path_out
@@ -3467,7 +3482,7 @@ def transmute(self, path, mutation_size, linewidth):
34673482
w1=1., wm=0.6, w2=0.3)
34683483

34693484
# path for head
3470-
in_f = inside_circle(x0, y0, tail_width * .3)
3485+
in_f = _inside_circle(x0, y0, tail_width * .3)
34713486
path_in, path_out = split_bezier_intersecting_with_closedpath(
34723487
arrow_path, in_f, tolerance=0.01)
34733488
tail_start = path_in[-1]

lib/matplotlib/path.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import matplotlib as mpl
1818
from . import _path, cbook
1919
from .cbook import _to_unmasked_float_array, simple_linear_interpolation
20+
from .bezier import split_bezier_intersecting_with_closedpath
2021

2122

2223
class Path:
@@ -586,6 +587,65 @@ def interpolated(self, steps):
586587
new_codes = None
587588
return Path(vertices, new_codes)
588589

590+
def split_path_inout(self, inside, tolerance=0.01, reorder_inout=False):
591+
"""
592+
Divide a path into two segments at the point where ``inside(x, y)``
593+
becomes False.
594+
"""
595+
path_iter = self.iter_segments()
596+
597+
ctl_points, command = next(path_iter)
598+
begin_inside = inside(ctl_points[-2:]) # true if begin point is inside
599+
600+
ctl_points_old = ctl_points
601+
602+
iold = 0
603+
i = 1
604+
605+
for ctl_points, command in path_iter:
606+
iold = i
607+
i += len(ctl_points) // 2
608+
if inside(ctl_points[-2:]) != begin_inside:
609+
bezier_path = np.concatenate([ctl_points_old[-2:], ctl_points])
610+
break
611+
ctl_points_old = ctl_points
612+
else:
613+
raise ValueError("The path does not intersect with the patch")
614+
615+
bp = bezier_path.reshape((-1, 2))
616+
left, right = split_bezier_intersecting_with_closedpath(
617+
bp, inside, tolerance)
618+
if len(left) == 2:
619+
codes_left = [Path.LINETO]
620+
codes_right = [Path.MOVETO, Path.LINETO]
621+
elif len(left) == 3:
622+
codes_left = [Path.CURVE3, Path.CURVE3]
623+
codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3]
624+
elif len(left) == 4:
625+
codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4]
626+
codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
627+
else:
628+
raise AssertionError("This should never be reached")
629+
630+
verts_left = left[1:]
631+
verts_right = right[:]
632+
633+
if self.codes is None:
634+
path_in = Path(np.concatenate([self.vertices[:i], verts_left]))
635+
path_out = Path(np.concatenate([verts_right, self.vertices[i:]]))
636+
637+
else:
638+
path_in = Path(np.concatenate([self.vertices[:iold], verts_left]),
639+
np.concatenate([self.codes[:iold], codes_left]))
640+
641+
path_out = Path(np.concatenate([verts_right, self.vertices[i:]]),
642+
np.concatenate([codes_right, self.codes[i:]]))
643+
644+
if reorder_inout and not begin_inside:
645+
path_in, path_out = path_out, path_in
646+
647+
return path_in, path_out
648+
589649
def to_polygons(self, transform=None, width=0, height=0, closed_only=True):
590650
"""
591651
Convert this path to a list of polygons or polylines. Each
@@ -648,7 +708,8 @@ def unit_rectangle(cls):
648708
def unit_regular_polygon(cls, numVertices):
649709
"""
650710
Return a :class:`Path` instance for a unit regular polygon with the
651-
given *numVertices* and radius of 1.0, centered at (0, 0).
711+
given *numVertices* such that the circumscribing circle has radius 1.0,
712+
centered at (0, 0).
652713
"""
653714
if numVertices <= 16:
654715
path = cls._unit_regular_polygons.get(numVertices)

0 commit comments

Comments
 (0)