@@ -1647,6 +1647,8 @@ def draw(self, renderer):
16471647 """
16481648 if not hasattr (self , 'axes' ):
16491649 raise RuntimeError ('Arcs can only be used in Axes instances' )
1650+ if not self .get_visible ():
1651+ return
16501652
16511653 self ._recompute_transform ()
16521654
@@ -1659,14 +1661,40 @@ def theta_stretch(theta, scale):
16591661 theta = np .deg2rad (theta )
16601662 x = np .cos (theta )
16611663 y = np .sin (theta )
1662- return np .rad2deg (np .arctan2 (scale * y , x ))
1663- theta1 = theta_stretch (self .theta1 , width / height )
1664- theta2 = theta_stretch (self .theta2 , width / height )
1665-
1666- # Get width and height in pixels
1667- width , height = self .get_transform ().transform ((width , height ))
1664+ stheta = np .rad2deg (np .arctan2 (scale * y , x ))
1665+ # arctan2 has the range [-pi, pi], we expect [0, 2*pi]
1666+ return (stheta + 360 ) % 360
1667+
1668+ theta1 = self .theta1
1669+ theta2 = self .theta2
1670+
1671+ if (
1672+ # if we need to stretch the angles because we are distorted
1673+ width != height
1674+ # and we are not doing a full circle.
1675+ #
1676+ # 0 and 360 do not exactly round-trip through the angle
1677+ # stretching (due to both float precision limitations and
1678+ # the difference between the range of arctan2 [-pi, pi] and
1679+ # this method [0, 360]) so avoid doing it if we don't have to.
1680+ and not (theta1 != theta2 and theta1 % 360 == theta2 % 360 )
1681+ ):
1682+ theta1 = theta_stretch (self .theta1 , width / height )
1683+ theta2 = theta_stretch (self .theta2 , width / height )
1684+
1685+ # Get width and height in pixels we need to use
1686+ # `self.get_data_transform` rather than `self.get_transform`
1687+ # because we want the transform from dataspace to the
1688+ # screen space to estimate how big the arc will be in physical
1689+ # units when rendered (the transform that we get via
1690+ # `self.get_transform()` goes from an idealized unit-radius
1691+ # space to screen space).
1692+ data_to_screen_trans = self .get_data_transform ()
1693+ pwidth , pheight = (data_to_screen_trans .transform ((width , height )) -
1694+ data_to_screen_trans .transform ((0 , 0 )))
16681695 inv_error = (1.0 / 1.89818e-6 ) * 0.5
1669- if width < inv_error and height < inv_error :
1696+
1697+ if pwidth < inv_error and pheight < inv_error :
16701698 self ._path = Path .arc (theta1 , theta2 )
16711699 return Patch .draw (self , renderer )
16721700
@@ -1700,29 +1728,32 @@ def segment_circle_intersect(x0, y0, x1, y1):
17001728 y0e , y1e = y0 , y1
17011729 xys = line_circle_intersect (x0 , y0 , x1 , y1 )
17021730 xs , ys = xys .T
1703- return xys [(x0e - epsilon < xs ) & (xs < x1e + epsilon )
1704- & (y0e - epsilon < ys ) & (ys < y1e + epsilon )]
1731+ return xys [
1732+ (x0e - epsilon < xs ) & (xs < x1e + epsilon )
1733+ & (y0e - epsilon < ys ) & (ys < y1e + epsilon )
1734+ ]
17051735
17061736 # Transforms the axes box_path so that it is relative to the unit
17071737 # circle in the same way that it is relative to the desired ellipse.
1708- box_path = Path .unit_rectangle ()
17091738 box_path_transform = (transforms .BboxTransformTo (self .axes .bbox )
17101739 + self .get_transform ().inverted ())
1711- box_path = box_path .transformed (box_path_transform )
1740+ box_path = Path . unit_rectangle () .transformed (box_path_transform )
17121741
17131742 thetas = set ()
17141743 # For each of the point pairs, there is a line segment
17151744 for p0 , p1 in zip (box_path .vertices [:- 1 ], box_path .vertices [1 :]):
17161745 xy = segment_circle_intersect (* p0 , * p1 )
17171746 x , y = xy .T
1718- theta = np .rad2deg (np .arctan2 (y , x ))
1747+ # arctan2 return [-pi, pi), the rest of our angles are in
1748+ # [0, 360], adjust as needed.
1749+ theta = (np .rad2deg (np .arctan2 (y , x )) + 360 ) % 360
17191750 thetas .update (theta [(theta1 < theta ) & (theta < theta2 )])
17201751 thetas = sorted (thetas ) + [theta2 ]
1721-
17221752 last_theta = theta1
17231753 theta1_rad = np .deg2rad (theta1 )
1724- inside = box_path .contains_point ((np .cos (theta1_rad ),
1725- np .sin (theta1_rad )))
1754+ inside = box_path .contains_point (
1755+ (np .cos (theta1_rad ), np .sin (theta1_rad ))
1756+ )
17261757
17271758 # save original path
17281759 path_original = self ._path
0 commit comments