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

Commit14d5441

Browse files
committed
FIX: update arc to do better rounding and allow wrap
1 parent492a478 commit14d5441

File tree

4 files changed

+103
-35
lines changed

4 files changed

+103
-35
lines changed

‎lib/matplotlib/path.py

Lines changed: 80 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -949,36 +949,73 @@ def unit_circle_righthalf(cls):
949949
returncls._unit_circle_righthalf
950950

951951
@classmethod
952-
defarc(cls,theta1,theta2,n=None,is_wedge=False):
952+
defarc(cls,theta1,theta2,n=None,is_wedge=False,wrap=True):
953953
"""
954-
Return a `Path` fortheunit circle arc from angles *theta1* to
955-
*theta2* (in degrees).
954+
Return a `Path` fora counter-clockwiseunit circle arc from angles
955+
*theta1* to *theta2* (in degrees).
956956
957-
*theta2* is unwrapped to produce the shortest arc within 360 degrees.
958-
That is, if *theta2* > *theta1* + 360, the arc will be from *theta1* to
959-
*theta2* - 360 and not a full circle plus some extra overlap.
960-
961-
If *n* is provided, it is the number of spline segments to make.
962-
If *n* is not provided, the number of spline segments is
963-
determined based on the delta between *theta1* and *theta2*.
957+
Parameters
958+
----------
959+
theta1, theta2 : float
960+
The angles (in degrees) defining the start (*theta1*) and end
961+
(*theta2*) of the arc. If the arc spans more than 360 degrees, it
962+
will be wrapped to fit within the range from *theta1* to *theta1* +
963+
360, provided *wrap* is True. The arc is drawn counter-clockwise
964+
from *theta1* to *theta2*. For instance, if *theta1* =90 and
965+
*theta2* = 70, the resulting arc will span 320 degrees.
966+
967+
n : int, optional
968+
The number of spline segments to make. If not provided, the number
969+
of spline segments is determined based on the delta between
970+
*theta1* and *theta2*.
971+
972+
is_wedge : bool, default: False
973+
If True, the arc is a wedge. The first vertex is the center of the
974+
wedge, the second vertex is the start of the arc, and the last
975+
vertex is the end of the arc. The wedge is closed with a line
976+
segment to the center of the wedge. If False, the arc is a
977+
polyline. The first vertex is the start of the arc, and the last
978+
vertex is the end of the arc. The arc is closed with a line
979+
segment to the start of the arc. The wedge is not closed with a
980+
line segment to the start of the arc.
981+
982+
wrap : bool, default: True
983+
If True, the arc is wrapped to fit between *theta1* and *theta1* +
984+
360 degrees. If False, the arc is not wrapped. The arc will be
985+
drawn from *theta1* to *theta2*.
964986
965-
Masionobe, L. 2003. `Drawing an elliptical arc using
966-
polylines, quadratic or cubic Bezier curves
987+
Notes
988+
-----
989+
The arc is approximated using cubic Bézier curves, as described in
990+
Masionobe, L. 2003. `Drawing an elliptical arc using polylines,
991+
quadratic or cubic Bezier curves
967992
<https://web.archive.org/web/20190318044212/http://www.spaceroots.org/documents/ellipse/index.html>`_.
968993
"""
969-
halfpi=np.pi*0.5
970994

971995
eta1=theta1
972-
eta2=theta2-360*np.floor((theta2-theta1)/360)
973-
# Ensure 2pi range is not flattened to 0 due to floating-point errors,
974-
# but don't try to expand existing 0 range.
975-
iftheta2!=theta1andeta2<=eta1:
976-
eta2+=360
996+
ifwrap:
997+
# Wrap theta2 to 0-360 degrees from theta1.
998+
eta2=np.mod(theta2-theta1,360.0)+theta1
999+
print('Eta1, Eta20',eta1,eta2)
1000+
# Ensure 360-deg range is not flattened to 0 due to floating-point
1001+
# errors, but don't try to expand existing 0 range.
1002+
iftheta2!=theta1andeta2<=eta1:
1003+
eta2+=360
1004+
print('Eta1, Eta2',eta1,eta2)
1005+
else:
1006+
eta2=theta2
9771007
eta1,eta2=np.deg2rad([eta1,eta2])
9781008

9791009
# number of curve segments to make
9801010
ifnisNone:
981-
n=int(2**np.ceil((eta2-eta1)/halfpi))
1011+
ifnp.abs(eta2-eta1)<=2.2*np.pi:
1012+
# this doesn't need to grow exponentially, but we have left
1013+
# this way for back compatibility
1014+
n=int(2**np.ceil(2*np.abs(eta2-eta1)/np.pi))
1015+
print('Here')
1016+
else:
1017+
# this will not grow exponentially if we allow wrapping arcs:
1018+
n=int(2*np.ceil(2*np.abs(eta2-eta1)/np.pi))
9821019
ifn<1:
9831020
raiseValueError("n must be >= 1 or None")
9841021

@@ -1028,22 +1065,32 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
10281065
returncls(vertices,codes,readonly=True)
10291066

10301067
@classmethod
1031-
defwedge(cls,theta1,theta2,n=None):
1068+
defwedge(cls,theta1,theta2,n=None,wrap=True):
10321069
"""
1033-
Return a `Path` for the unit circle wedge from angles *theta1* to
1034-
*theta2* (in degrees).
1035-
1036-
*theta2* is unwrapped to produce the shortest wedge within 360 degrees.
1037-
That is, if *theta2* > *theta1* + 360, the wedge will be from *theta1*
1038-
to *theta2* - 360 and not a full circle plus some extra overlap.
1039-
1040-
If *n* is provided, it is the number of spline segments to make.
1041-
If *n* is not provided, the number of spline segments is
1042-
determined based on the delta between *theta1* and *theta2*.
1070+
Return a `Path` for a counter-clockwise unit circle wedge from angles
1071+
*theta1* to *theta2* (in degrees).
10431072
1044-
See `Path.arc` for the reference on the approximation used.
1045-
"""
1046-
returncls.arc(theta1,theta2,n,True)
1073+
Parameters
1074+
----------
1075+
theta1, theta2 : float
1076+
The angles (in degrees) defining the start (*theta1*) and end
1077+
(*theta2*) of the arc. If the arc spans more than 360 degrees, it
1078+
will be wrapped to fit within the range from *theta1* to *theta1* +
1079+
360, provided *wrap* is True. The arc is drawn counter-clockwise
1080+
from *theta1* to *theta2*. For instance, if *theta1* =90 and
1081+
*theta2* = 70, the resulting arc will span 320 degrees.
1082+
1083+
n : int, optional
1084+
The number of spline segments to make. If not provided, the number
1085+
of spline segments is determined based on the delta between
1086+
*theta1* and *theta2*.
1087+
1088+
wrap : bool, default: True
1089+
If True, the arc is wrapped to fit between *theta1* and *theta1* +
1090+
360 degrees. If False, the arc is not wrapped. The arc will be
1091+
drawn from *theta1* to *theta2*.
1092+
"""
1093+
returncls.arc(theta1,theta2,n,wedge=True,wrap=wrap)
10471094

10481095
@staticmethod
10491096
@lru_cache(8)

‎lib/matplotlib/path.pyi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,14 @@ class Path:
119119
defunit_circle_righthalf(cls)->Path: ...
120120
@classmethod
121121
defarc(
122-
cls,theta1:float,theta2:float,n:int|None= ...,is_wedge:bool= ...
122+
cls,theta1:float,theta2:float,n:int|None= ...,
123+
is_wedge:bool= ...,wrap:bool= ...
123124
)->Path: ...
124125
@classmethod
125-
defwedge(cls,theta1:float,theta2:float,n:int|None= ...)->Path: ...
126+
defwedge(
127+
cls,theta1:float,theta2:float,n:int|None= ...,
128+
wrap:bool= ...
129+
)->Path: ...
126130
@overload
127131
@staticmethod
128132
defhatch(hatchpattern:str,density:float= ...)->Path: ...

‎lib/matplotlib/tests/test_path.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,12 +471,29 @@ def test_full_arc(offset):
471471
high=360+offset
472472

473473
path=Path.arc(low,high)
474+
print(path.vertices)
474475
mins=np.min(path.vertices,axis=0)
475476
maxs=np.max(path.vertices,axis=0)
476477
np.testing.assert_allclose(mins,-1)
477478
np.testing.assert_allclose(maxs,1)
478479

479480

481+
@image_comparison(['arc_close360'],style='default',remove_text=True,
482+
extensions=['png'])
483+
deftest_arc_close360():
484+
fig,ax=plt.subplots(ncols=3)
485+
ax[0].add_patch(patches.PathPatch(Path.arc(theta1=-90-1e-14,theta2=270)))
486+
#ax[0].set_title("arc(-90-1e-14, 270), should be a circle")
487+
ax[1].add_patch(patches.PathPatch(Path.arc(theta1=-90,theta2=270)))
488+
#ax[1].set_title("arc(-90, 270), is a circle")
489+
ax[2].add_patch(patches.PathPatch(Path.arc(theta1=-90-1e-14,theta2=-90)))
490+
#ax[2].set_title("arc(-90, -90-1e-14), should not be a circle")
491+
forainax:
492+
a.set_xlim(-1,1)
493+
a.set_ylim(-1,1)
494+
a.set_aspect("equal")
495+
496+
480497
deftest_disjoint_zero_length_segment():
481498
this_path=Path(
482499
np.array([

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp