@@ -1607,6 +1607,8 @@ def draw(self, renderer):
16071607 """
16081608 if not hasattr (self , 'axes' ):
16091609 raise RuntimeError ('Arcs can only be used in Axes instances' )
1610+ if not self .get_visible ():
1611+ return
16101612
16111613 self ._recompute_transform ()
16121614
@@ -1619,14 +1621,40 @@ def theta_stretch(theta, scale):
16191621 theta = np .deg2rad (theta )
16201622 x = np .cos (theta )
16211623 y = np .sin (theta )
1622- return np .rad2deg (np .arctan2 (scale * y , x ))
1623- theta1 = theta_stretch (self .theta1 , width / height )
1624- theta2 = theta_stretch (self .theta2 , width / height )
1625-
1626- # Get width and height in pixels
1627- width , height = self .get_transform ().transform ((width , height ))
1624+ stheta = np .rad2deg (np .arctan2 (scale * y , x ))
1625+ # arctan2 has the range [-pi, pi], we expect [0, 2*pi]
1626+ return (stheta + 360 ) % 360
1627+
1628+ theta1 = self .theta1
1629+ theta2 = self .theta2
1630+
1631+ if (
1632+ # if we need to stretch the angles because we are distorted
1633+ width != height
1634+ # and we are not doing a full circle.
1635+ #
1636+ # 0 and 360 do not exactly round-trip through the angle
1637+ # stretching (due to both float precision limitations and
1638+ # the difference between the range of arctan2 [-pi, pi] and
1639+ # this method [0, 360]) so avoid doing it if we don't have to.
1640+ and not (theta1 != theta2 and theta1 % 360 == theta2 % 360 )
1641+ ):
1642+ theta1 = theta_stretch (self .theta1 , width / height )
1643+ theta2 = theta_stretch (self .theta2 , width / height )
1644+
1645+ # Get width and height in pixels we need to use
1646+ # `self.get_data_transform` rather than `self.get_transform`
1647+ # because we want the transform from dataspace to the
1648+ # screen space to estimate how big the arc will be in physical
1649+ # units when rendered (the transform that we get via
1650+ # `self.get_transform()` goes from an idealized unit-radius
1651+ # space to screen space).
1652+ data_to_screen_trans = self .get_data_transform ()
1653+ pwidth , pheight = (data_to_screen_trans .transform ((width , height )) -
1654+ data_to_screen_trans .transform ((0 , 0 )))
16281655 inv_error = (1.0 / 1.89818e-6 ) * 0.5
1629- if width < inv_error and height < inv_error :
1656+
1657+ if pwidth < inv_error and pheight < inv_error :
16301658 self ._path = Path .arc (theta1 , theta2 )
16311659 return Patch .draw (self , renderer )
16321660
@@ -1660,29 +1688,32 @@ def segment_circle_intersect(x0, y0, x1, y1):
16601688 y0e , y1e = y0 , y1
16611689 xys = line_circle_intersect (x0 , y0 , x1 , y1 )
16621690 xs , ys = xys .T
1663- return xys [(x0e - epsilon < xs ) & (xs < x1e + epsilon )
1664- & (y0e - epsilon < ys ) & (ys < y1e + epsilon )]
1691+ return xys [
1692+ (x0e - epsilon < xs ) & (xs < x1e + epsilon )
1693+ & (y0e - epsilon < ys ) & (ys < y1e + epsilon )
1694+ ]
16651695
16661696 # Transforms the axes box_path so that it is relative to the unit
16671697 # circle in the same way that it is relative to the desired ellipse.
1668- box_path = Path .unit_rectangle ()
16691698 box_path_transform = (transforms .BboxTransformTo (self .axes .bbox )
1670- - self .get_transform ())
1671- box_path = box_path .transformed (box_path_transform )
1699+ + self .get_transform (). inverted ())
1700+ box_path = Path . unit_rectangle () .transformed (box_path_transform )
16721701
16731702 thetas = set ()
16741703 # For each of the point pairs, there is a line segment
16751704 for p0 , p1 in zip (box_path .vertices [:- 1 ], box_path .vertices [1 :]):
16761705 xy = segment_circle_intersect (* p0 , * p1 )
16771706 x , y = xy .T
1678- theta = np .rad2deg (np .arctan2 (y , x ))
1707+ # arctan2 return [-pi, pi), the rest of our angles are in
1708+ # [0, 360], adjust as needed.
1709+ theta = (np .rad2deg (np .arctan2 (y , x )) + 360 ) % 360
16791710 thetas .update (theta [(theta1 < theta ) & (theta < theta2 )])
16801711 thetas = sorted (thetas ) + [theta2 ]
1681-
16821712 last_theta = theta1
16831713 theta1_rad = np .deg2rad (theta1 )
1684- inside = box_path .contains_point ((np .cos (theta1_rad ),
1685- np .sin (theta1_rad )))
1714+ inside = box_path .contains_point (
1715+ (np .cos (theta1_rad ), np .sin (theta1_rad ))
1716+ )
16861717
16871718 # save original path
16881719 path_original = self ._path
0 commit comments