8000 refactor Bezier so it doesn't depend on Path · matplotlib/matplotlib@111645a · GitHub
[go: up one dir, main page]

Skip to content

Commit 111645a

Browse files
committed
refactor Bezier so it doesn't depend on Path
1 parent 72add05 commit 111645a

File tree

5 files changed

+165
-153
lines changed

5 files changed

+165
-153
lines changed

doc/api/next_api_changes/deprecations.rst

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -227,19 +227,9 @@ The following validators, defined in `.rcsetup`, are deprecated:
227227
``validate_axes_titlelocation``, ``validate_toolbar``,
228228
``validate_ps_papersize``, ``validate_legend_loc``,
229229
``validate_bool_maybe_none``, ``validate_hinting``,
230-
``validate_movie_writers``.
230+
``validate_movie_writers``, ``validate_webagg_address``.
231231
To test whether an rcParam value would be acceptable, one can test e.g. ``rc =
232232
RcParams(); rc[k] = v`` raises an exception.
233-
||||||| constructed merge base
234-
``validate_ps_papersize``, ``validate_legend_log``. To test whether an rcParam
235-
value would be acceptable, one can test e.g. ``rc = RcParams(); rc[k] = v``
236-
raises an exception.
237-
=======
238-
``validate_ps_papersize``, ``validate_legend_loc``,
239-
``validate_webagg_address``.
240-
To test whether an rcParam value would be acceptable, one can test e.g. ``rc =
241-
RcParams(); rc[k] = v`` raises an exception.
242-
>>>>>>> Deprecate validate_webagg_address.
243233

244234
Stricter rcParam validation
245235
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -379,10 +369,10 @@ also be accessible as ``toolbar.parent()``.
379369

380370
Path helpers in :mod:`.bezier`
381371
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382-
383-
``bezier.make_path_regular`` is deprecated. Use ``Path.cleaned()`` (or
384-
``Path.cleaned(curves=True)``, etc.) instead (but note that these methods add a
385-
``STOP`` code at the end of the path).
386-
387-
``bezier.concatenate_paths`` is deprecated. Use ``Path.make_compound_path()``
388-
instead.
372+
- ``bezier.make_path_regular`` is deprecated. Use ``Path.cleaned()`` (or
373+
``Path.cleaned(curves=True)``, etc.) instead (but note that these methods add
374+
a ``STOP`` code at the end of the path).
375+
- ``bezier.concatenate_paths`` is deprecated. Use ``Path.make_compound_path()``
376+
instead.
377+
- ``bezier.split_path_inout`` (use ``Path.split_path_inout`` instead)
378+
- ``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
@@ -92,7 +92,7 @@ Classes and methods
9292

9393
- ``image.BboxImage.interp_at_native`` property (no replacement)
9494
- ``lines.Line2D.verticalOffset`` property (no replacement)
95-
- ``bezier.find_r_to_boundary_of_closedpath()`` (no relacement)
95+
- ``bezier.find_r_to_boundary_of_closedpath()`` (no replacement)
9696

9797
- ``quiver.Quiver.color()`` (use ``Quiver.get_facecolor()`` instead)
9898
- ``quiver.Quiver.keyvec`` property (no replacement)

lib/matplotlib/bezier.py

Lines changed: 57 additions & 114 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,54 @@ def get_normal_points(cx, cy, cos_t, sin_t, length):
6868
return x1, y1, x2, y2
6969

7070

71+
# deprecated routines (moved to `.Path`)
72+
73+
74+
@cbook.deprecated("3.2")
75+
def inside_circle(cx, cy, r):
76+
"""
77+
Return a function that checks whether a point is in a circle with center
78+
(*cx*, *cy*) and radius *r*.
79+
80+
The returned function has the signature::
81+
82+
f(xy: Tuple[float, float]) -> bool
83+
"""
84+
r2 = r ** 2
85+
86+
def _f(xy):
87+
x, y = xy
88+
return (x - cx) ** 2 + (y - cy) ** 2 < r2
89+
return _f
90+
91+
92+
@cbook.deprecated("3.2", alternative="Path.make_compound_path()")
93+
def split_path_inout(path, inside, tolerance=0.01, reorder_inout=False):
94+
"""
95+
Divide a path into two segments at the point where ``inside(x, y)``
96+
becomes False.
97+
"""
98+
return path.split_path_inout(inside, tolerance, reorder_inout)
99+
100+
101+
@cbook.deprecated(
102+
"3.2", alternative="Path.cleaned() and remove the final STOP if needed")
103+
def make_path_regular(path):
104+
"""
105+
If the ``codes`` attribute of `.Path` *p* is None, return a copy of *p*
106+
with ``codes`` set to (MOVETO, LINETO, LINETO, ..., LINETO); otherwise
107+
return *p* itself.
108+
"""
109+
return path.make_path_regular()
110+
111+
112+
@cbook.deprecated("3.2", alternative="Path.make_compound_path()")
113+
def concatenate_paths(paths):
114+
"""Concatenate a list of paths into a single path."""
115+
from .path import Path
116+
return Path.make_compound_path(*paths)
117+
118+
71119
# BEZIER routines
72120

73121
# subdividing bezier curve
@@ -177,12 +225,13 @@ class BezierSegment:
177225
"""
178226

179227
def __init__(self, control_points):
180-
n = len(control_points)
181-
self._orders = np.arange(n)
182-
coeff = [math.factorial(n - 1)
183-
// (math.factorial(i) * math.factorial(n - 1 - i))
184-
for i in range(n)]
185-
self._px = np.asarray(control_points).T * coeff
228+
self.cpoints = np.asarray(control_points)
229+
self.n, self.d = self.cpoints.shape
230+
self._orders = np.arange(self.n)
231+
coeff = [math.factorial(self.n - 1)
232+
// (math.factorial(i) * math.factorial(self.n - 1 - i))
233+
for i in range(self.n)]
234+
self._px = self.cpoints.T * coeff
186235

187236
def point_at_t(self, t):
188237
"""Return the point on the Bezier curve for parameter *t*."""
@@ -222,90 +271,9 @@ def split_bezier_intersecting_with_closedpath(
222271
return _left, _right
223272

224273

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-
concat = np.concatenate
241-
242-
iold = 0
243-
i = 1
244-
245-
for ctl_points, command in path_iter:
246-
iold = i
247-
i += len(ctl_points) // 2
248-
if inside(ctl_points[-2:]) != begin_inside:
249-
bezier_path = concat([ctl_points_old[-2:], ctl_points])
250-
break
251-
ctl_points_old = ctl_points
252-
else:
253-
raise ValueError("The path does not intersect with the patch")
254-
255-
bp = bezier_path.reshape((-1, 2))
256-
left, right = split_bezier_intersecting_with_closedpath(
257-
bp, inside, tolerance)
258-
if len(left) == 2:
259-
codes_left = [Path.LINETO]
260-
codes_right = [Path.MOVETO, Path.LINETO]
261-
elif len(left) == 3:
262-
codes_left = [Path.CURVE3, Path.CURVE3]
263-
codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3]
264-
elif len(left) == 4:
265-
codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4]
266-
codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
267-
else:
268-
raise AssertionError("This should never be reached")
269-
270-
verts_left = left[1:]
271-
verts_right = right[:]
272-
273-
if path.codes is None:
274-
path_in = Path(concat([path.vertices[:i], verts_left]))
275-
path_out = Path(concat([verts_right, path.vertices[i:]]))
276-
277-
else:
278-
path_in = Path(concat([path.vertices[:iold], verts_left]),
279-
concat([path.codes[:iold], codes_left]))
280-
281-
path_out = Path(concat([verts_right, path.vertices[i:]]),
282-
concat([codes_right, path.codes[i:]]))
283-
284-
if reorder_inout and not begin_inside:
285-
path_in, path_out = path_out, path_in
286-
287-
return path_in, path_out
288-
289-
290-
def inside_circle(cx, cy, r):
291-
"""
292-
Return a function that checks whether a point is in a circle with center
293-
(*cx*, *cy*) and radius *r*.
294-
295-
The returned function has the signature::
296-
297-
f(xy: Tuple[float, float]) -> bool
298-
"""
299-
r2 = r ** 2
300-
301-
def _f(xy):
302-
x, y = xy
303-
return (x - cx) ** 2 + (y - cy) ** 2 < r2
304-
return _f
305-
306-
307274
# quadratic Bezier lines
308275

276+
309277
def get_cos_sin(x0, y0, x1, y1):
310278
dx, dy = x1 - x0, y1 - y0
311279
d = (dx * dx + dy * dy) ** .5
@@ -478,28 +446,3 @@ def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.):
478446
c3x_right, c3y_right)
479447

480448
return path_left, path_right
481-
482-
483-
@cbook.deprecated(
484-
"3.2", alternative="Path.cleaned() and remove the final STOP if needed")
485-
def make_path_regular(p):
486-
"""
487-
If the ``codes`` attribute of `.Path` *p* is None, return a copy of *p*
488-
with ``codes`` set to (MOVETO, LINETO, LINETO, ..., LINETO); otherwise
489-
return *p* itself.
490-
"""
491-
c = p.codes
492-
if c is None:
493-
c = np.full(len(p.vertices), Path.LINETO, dtype=Path.code_type)
494-
c[0] = Path.MOVETO
495-
return Path(p.vertices, c)
496-
else:
497-
return p
498-
499-
500-
@cbook.deprecated("3.2", alternative="Path.make_compound_path()")
501-
def concatenate_paths(paths):
502-
"""Concatenate a list of paths into a single path."""
503-
vertices = np.concatenate([p.vertices for p in paths])
504-
codes = np.concatenate([make_path_regular(p).codes for p in paths])
505-
return Path(vertices, codes)

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

24152431
try:
2416-
left, right = split_path_inout(path, insideA)
2432+
left, right = path.split_path_inout(insideA)
24172433
except ValueError:
24182434
right = path
24192435

@@ -2425,7 +2441,7 @@ def insideB(xy_display):
24252441
return patchB.contains(xy_event)[0]
24262442

24272443
try:
2428-
left, right = split_path_inout(path, insideB)
2444+
left, right = path.split_path_inout(insideB)
24292445
except ValueError:
24302446
left = path
24312447

@@ -2438,15 +2454,15 @@ def _shrink(self, path, shrinkA, shrinkB):
24382454
Shrink the path by fixed size (in points) with shrinkA and shrinkB.
24392455
"""
24402456
if shrinkA:
2441-
insideA = inside_circle(*path.vertices[0], shrinkA)
2457+
insideA = _inside_circle(*path.vertices[0], shrinkA)
24422458
try:
2443-
left, path = split_path_inout(path, insideA)
2459+
left, path = path.split_path_inout(insideA)
24442460
except ValueError:
24452461
pass
24462462
if shrinkB:
2447-
insideB = inside_circle(*path.vertices[-1], shrinkB)
2463+
insideB = _inside_circle(*path.vertices[-1], shrinkB)
24482464
try:
2449-
path, right = split_path_inout(path, insideB)
2465+
path, right = path.split_path_inout(insideB)
24502466
except ValueError:
24512467
pass
24522468
return path
@@ -2871,7 +2887,6 @@ def __call__(self, path, mutation_size, linewidth,
28712887
The __call__ method is a thin wrapper around the transmute method
28722888
and takes care of the aspect ratio.
28732889
"""
2874-
28752890
if aspect_ratio is not None:
28762891
# Squeeze the given height by the aspect_ratio
28772892
vertices = path.vertices / [1, aspect_ratio]
@@ -3336,7 +3351,7 @@ def transmute(self, path, mutation_size, linewidth):
33363351

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

33423357
try:
@@ -3419,7 +3434,7 @@ def transmute(self, path, mutation_size, linewidth):
34193434
arrow_path = [(x0, y0), (x1, y1), (x2, y2)]
34203435

34213436
# path for head
3422-
in_f = inside_circle(x2, y2, head_length)
3437+
in_f = _inside_circle(x2, y2, head_length)
34233438
try:
34243439
path_out, path_in = split_bezier_intersecting_with_closedpath(
34253440
arrow_path, in_f, tolerance=0.01)
@@ -3434,7 +3449,7 @@ def transmute(self, path, mutation_size, linewidth):
34343449
path_head = path_in
34353450

34363451
# path for head
3437-
in_f = inside_circle(x2, y2, head_length * .8)
3452+
in_f = _inside_circle(x2, y2, head_length * .8)
34383453
path_out, path_in = split_bezier_intersecting_with_closedpath(
34393454
arrow_path, in_f, tolerance=0.01)
34403455
path_tail = path_out
@@ -3452,7 +3467,7 @@ def transmute(self, path, mutation_size, linewidth):
34523467
w1=1., wm=0.6, w2=0.3)
34533468

34543469
# path for head
3455-
in_f = inside_circle(x0, y0, tail_width * .3)
3470+
in_f = _inside_circle(x0, y0, tail_width * .3)
34563471
path_in, path_out = split_bezier_intersecting_with_closedpath(
34573472
arrow_path, in_f, tolerance=0.01)
34583473
tail_start = path_in[-1]

0 commit comments

Comments
 (0)
0