Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite2f403a

Browse files
committed
code to compute bezier segment / path lengths
1 parent0037abd commite2f403a

File tree

4 files changed

+129
-1
lines changed

4 files changed

+129
-1
lines changed

‎lib/matplotlib/bezier.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
importmath
66
importwarnings
7+
fromcollectionsimportdeque
78

89
importnumpyasnp
910

@@ -206,6 +207,96 @@ def point_at_t(self, t):
206207
"""Return the point on the Bezier curve for parameter *t*."""
207208
returntuple(self(t))
208209

210+
defsplit_at_t(self,t):
211+
"""Split into two Bezier curves using de casteljau's algorithm.
212+
213+
Parameters
214+
----------
215+
t : float
216+
Point in [0,1] at which to split into two curves
217+
218+
Returns
219+
-------
220+
B1, B2 : BezierSegment
221+
The two sub-curves.
222+
"""
223+
new_cpoints=split_de_casteljau(self._cpoints,t)
224+
returnBezierSegment(new_cpoints[0]),BezierSegment(new_cpoints[1])
225+
226+
defcontrol_net_length(self):
227+
"""Sum of lengths between control points"""
228+
L=0
229+
N,d=self._cpoints.shape
230+
foriinrange(N-1):
231+
L+=np.linalg.norm(self._cpoints[i+1]-self._cpoints[i])
232+
returnL
233+
234+
defarc_length(self,rtol=None,atol=None):
235+
"""Estimate the length using iterative refinement.
236+
237+
Our estimate is just the average between the length of the chord and
238+
the length of the control net.
239+
240+
Since the chord length and control net give lower and upper bounds
241+
(respectively) on the length, this maximum possible error is tested
242+
against an absolute tolerance threshold at each subdivision.
243+
244+
However, sometimes this estimator converges much faster than this error
245+
esimate would suggest. Therefore, the relative change in the length
246+
estimate between subdivisions is compared to a relative error tolerance
247+
after each set of subdivisions.
248+
249+
Parameters
250+
----------
251+
rtol : float, default 1e-4
252+
If :code:`abs(est[i+1] - est[i]) <= rtol * est[i+1]`, we return
253+
:code:`est[i+1]`.
254+
atol : float, default 1e-6
255+
If the distance between chord length and control length at any
256+
point falls below this number, iteration is terminated.
257+
"""
258+
ifrtolisNone:
259+
rtol=1e-4
260+
ifatolisNone:
261+
atol=1e-6
262+
263+
chord=np.linalg.norm(self._cpoints[-1]-self._cpoints[0])
264+
net=self.control_net_length()
265+
max_err= (net-chord)/2
266+
curr_est=chord+max_err
267+
# early exit so we don't try to "split" paths of zero length
268+
ifmax_err<atol:
269+
returncurr_est
270+
271+
prev_est=np.inf
272+
curves=deque([self])
273+
errs=deque([max_err])
274+
lengths=deque([curr_est])
275+
whilenp.abs(curr_est-prev_est)>rtol*curr_est:
276+
# subdivide the *whole* curve before checking relative convergence
277+
# again
278+
prev_est=curr_est
279+
num_curves=len(curves)
280+
foriinrange(num_curves):
281+
curve=curves.popleft()
282+
new_curves=curve.split_at_t(0.5)
283+
max_err-=errs.popleft()
284+
curr_est-=lengths.popleft()
285+
forncurveinnew_curves:
286+
chord=np.linalg.norm(
287+
ncurve._cpoints[-1]-ncurve._cpoints[0])
288+
net=ncurve.control_net_length()
289+
nerr= (net-chord)/2
290+
nlength=chord+nerr
291+
max_err+=nerr
292+
curr_est+=nlength
293+
curves.append(ncurve)
294+
errs.append(nerr)
295+
lengths.append(nlength)
296+
ifmax_err<atol:
297+
returncurr_est
298+
returncurr_est
299+
209300
defarc_area(self):
210301
r"""
211302
(Signed) area swept out by ray from origin to curve.

‎lib/matplotlib/path.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,27 @@ def intersects_bbox(self, bbox, filled=True):
642642
return_path.path_intersects_rectangle(
643643
self,bbox.x0,bbox.y0,bbox.x1,bbox.y1,filled)
644644

645+
deflength(self,rtol=None,atol=None,**kwargs):
646+
"""Get length of Path.
647+
648+
Equivalent to (but not computed as)
649+
650+
.. math::
651+
652+
\sum_{j=1}^N \int_0^1 ||B'_j(t)|| dt
653+
654+
where the sum is over the :math:`N` Bezier curves that comprise the
655+
Path. Notice that this measure of length will assign zero weight to all
656+
isolated points on the Path.
657+
658+
Returns
659+
-------
660+
length : float
661+
The path length.
662+
"""
663+
returnnp.sum([B.arc_length(rtol,atol)
664+
forB,codeinself.iter_bezier(**kwargs)])
665+
645666
defsigned_area(self,**kwargs):
646667
"""
647668
Get signed area filled by path.

‎lib/matplotlib/tests/test_bezier.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
]
2121

2222

23+
def_integral_arc_length(B):
24+
dB=B.differentiate(B)
25+
defintegrand(t):
26+
returnnp.linalg.norm(dB(t))
27+
returnscipy.integrate.quad(integrand,0,1)[0]
28+
29+
2330
def_integral_arc_area(B):
2431
"""(Signed) area swept out by ray from origin to curve."""
2532
dB=B.differentiate(B)
@@ -33,3 +40,9 @@ def integrand(t):
3340
deftest_area_formula():
3441
forBintest_curves:
3542
assert(np.isclose(_integral_arc_area(B),B.arc_area()))
43+
44+
45+
deftest_length_iteration():
46+
forBintest_curves:
47+
assert(np.isclose(_integral_arc_length(B),B.arc_length(
48+
rtol=1e-5,atol=1e-8),rtol=1e-5,atol=1e-8))

‎lib/matplotlib/tests/test_path.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
importcopy
22

33
importnumpyasnp
4-
54
fromnumpy.testingimportassert_array_equal
65
importpytest
76

@@ -72,6 +71,10 @@ def test_signed_area_unit_rectangle():
7271
assert(np.isclose(rect.signed_area(),1))
7372

7473

74+
deftest_length_curve():
75+
assert(np.isclose(_hard_curve.length(),2.0))
76+
77+
7578
deftest_point_in_path_nan():
7679
box=np.array([[0,0], [1,0], [1,1], [0,1], [0,0]])
7780
p=Path(box)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp