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

Skip to content

Commit d31d0cb

Browse files
committed
refactor Bezier so it doesn't depend on Path
1 parent 0e4685b commit d31d0cb

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,13 +374,13 @@ 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)
384384

385385
``animation.html_args`` rcParam
386386
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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"],
@@ -2414,7 +2430,7 @@ def insideA(xy_display):
24142430
return patchA.contains(xy_event)[0]
24152431

24162432
try:
2417-
left, right = split_path_inout(path, insideA)
2433+
left, right = path.split_path_inout(insideA)
24182434
except ValueError:
24192435
right = path
24202436

@@ -2426,7 +2442,7 @@ def insideB(xy_display):
24262442
return patchB.contains(xy_event)[0]
24272443

24282444
try:
2429-
left, right = split_path_inout(path, insideB)
2445+
left, right = path.split_path_inout(insideB)
24302446
except ValueError:
24312447
left = path
24322448

@@ -2439,15 +2455,15 @@ def _shrink(self, path, shrinkA, shrinkB):
24392455
Shrink the path by fixed size (in points) with shrinkA and shrinkB.
24402456
"""
24412457
if shrinkA:
2442-
insideA = inside_circle(*path.vertices[0], shrinkA)
2458+
insideA = _inside_circle(*path.vertices[0], shrinkA)
24432459
try:
2444-
left, path = split_path_inout(path, insideA)
2460+
left, path = path.split_path_inout(insideA)
24452461
except ValueError:
24462462
pass
24472463
if shrinkB:
2448-
insideB = inside_circle(*path.vertices[-1], shrinkB)
2464+
insideB = _inside_circle(*path.vertices[-1], shrinkB)
24492465
try:
2450-
path, right = split_path_inout(path, insideB)
2466+
path, right = path.split_path_inout(insideB)
24512467
except ValueError:
24522468
pass
24532469
return path
@@ -2872,7 +2888,6 @@ def __call__(self, path, mutation_size, linewidth,
28722888
The __call__ method is a thin wrapper around the transmute method
28732889
and takes care of the aspect ratio.
28742890
"""
2875-
28762891
if aspect_ratio is not None:
28772892
# Squeeze the given height by the aspect_ratio
28782893
vertices = path.vertices / [1, aspect_ratio]
@@ -3337,7 +3352,7 @@ def transmute(self, path, mutation_size, linewidth):
33373352

33383353
# divide the path into a head and a tail
33393354
head_length = self.head_length * mutation_size
3340-
in_f = inside_circle(x2, y2, head_length)
3355+
in_f = _inside_circle(x2, y2, head_length)
33413356
arrow_path = [(x0, y0), (x1, y1), (x2, y2)]
33423357

33433358
try:
@@ -3420,7 +3435,7 @@ def transmute(self, path, mutation_size, linewidth):
34203435
arrow_path = [(x0, y0), (x1, y1), (x2, y2)]
34213436

34223437
# path for head
3423-
in_f = inside_circle(x2, y2, head_length)
3438+
in_f = _inside_circle(x2, y2, head_length)
34243439
try:
34253440
path_out, path_in = split_bezier_intersecting_with_closedpath(
34263441
arrow_path, in_f, tolerance=0.01)
@@ -3435,7 +3450,7 @@ def transmute(self, path, mutation_size, linewidth):
34353450
path_head = path_in
34363451

34373452
# path for head
3438-
in_f = inside_circle(x2, y2, head_length * .8)
3453+
in_f = _inside_circle(x2, y2, head_length * .8)
34393454
path_out, path_in = split_bezier_intersecting_with_closedpath(
34403455
arrow_path, in_f, tolerance=0.01)
34413456
path_tail = path_out
@@ -3453,7 +3468,7 @@ def transmute(self, path, mutation_size, linewidth):
34533468
w1=1., wm=0.6, w2=0.3)
34543469

34553470
# path for head
3456-
in_f = inside_circle(x0, y0, tail_width * .3)
3471+
in_f = _inside_circle(x0, y0, tail_width * .3)
34573472
path_in, path_out = split_bezier_intersecting_with_closedpath(
34583473
arrow_path, in_f, tolerance=0.01)
34593474
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:
@@ -585,6 +586,65 @@ def interpolated(self, steps):
585586
new_codes = None
586587
return Path(vertices, new_codes)
587588

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

0 commit comments

Comments
 (0)