@@ -1592,14 +1592,12 @@ def draw(self, renderer):
15921592 calculation much easier than doing rotated ellipse
15931593 intersection directly).
15941594
1595- This uses the "line intersecting a circle" algorithm
1596- from:
1595+ This uses the "line intersecting a circle" algorithm from:
15971596
15981597 Vince, John. *Geometry for Computer Graphics: Formulae,
15991598 Examples & Proofs.* London: Springer-Verlag, 2005.
16001599
1601- 2. The angles of each of the intersection points are
1602- calculated.
1600+ 2. The angles of each of the intersection points are calculated.
16031601
16041602 3. Proceeding counterclockwise starting in the positive
16051603 x-direction, each of the visible arc-segments between the
@@ -1632,33 +1630,25 @@ def theta_stretch(theta, scale):
16321630 self ._path = Path .arc (theta1 , theta2 )
16331631 return Patch .draw (self , renderer )
16341632
1635- def iter_circle_intersect_on_line (x0 , y0 , x1 , y1 ):
1633+ def line_circle_intersect (x0 , y0 , x1 , y1 ):
16361634 dx = x1 - x0
16371635 dy = y1 - y0
16381636 dr2 = dx * dx + dy * dy
16391637 D = x0 * y1 - x1 * y0
16401638 D2 = D * D
16411639 discrim = dr2 - D2
1642-
1643- # Single (tangential) intersection
1644- if discrim == 0.0 :
1645- x = (D * dy ) / dr2
1646- y = (- D * dx ) / dr2
1647- yield x , y
1648- elif discrim > 0.0 :
1649- # The definition of "sign" here is different from
1650- # np.sign: we never want to get 0.0
1651- if dy < 0.0 :
1652- sign_dy = - 1.0
1653- else :
1654- sign_dy = 1.0
1640+ if discrim >= 0.0 :
1641+ sign_dy = np .copysign (1 , dy ) # +/-1, never 0.
16551642 sqrt_discrim = np .sqrt (discrim )
1656- for sign in (1. , - 1. ):
1657- x = (D * dy + sign * sign_dy * dx * sqrt_discrim ) / dr2
1658- y = (- D * dx + sign * np .abs (dy ) * sqrt_discrim ) / dr2
1659- yield x , y
1643+ return np .array (
1644+ [[(D * dy + sign_dy * dx * sqrt_discrim ) / dr2 ,
1645+ (- D * dx + abs (dy ) * sqrt_discrim ) / dr2 ],
1646+ [(D * dy - sign_dy * dx * sqrt_discrim ) / dr2 ,
1647+ (- D * dx - abs (dy ) * sqrt_discrim ) / dr2 ]])
1648+ else :
1649+ return np .empty ((0 , 2 ))
16601650
1661- def iter_circle_intersect_on_line_seg (x0 , y0 , x1 , y1 ):
1651+ def segment_circle_intersect (x0 , y0 , x1 , y1 ):
16621652 epsilon = 1e-9
16631653 if x1 < x0 :
16641654 x0e , x1e = x1 , x0
@@ -1668,13 +1658,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16681658 y0e , y1e = y1 , y0
16691659 else :
16701660 y0e , y1e = y0 , y1
1671- x0e -= epsilon
1672- y0e -= epsilon
1673- x1e += epsilon
1674- y1e += epsilon
1675- for x , y in iter_circle_intersect_on_line (x0 , y0 , x1 , y1 ):
1676- if x0e <= x <= x1e and y0e <= y <= y1e :
1677- yield x , y
1661+ xys = line_circle_intersect (x0 , y0 , x1 , y1 )
1662+ xs , ys = xys .T
1663+ return xys [(x0e - epsilon < xs ) & (xs < x1e + epsilon )
1664+ & (y0e - epsilon < ys ) & (ys < y1e + epsilon )]
16781665
16791666 # Transforms the axes box_path so that it is relative to the unit
16801667 # circle in the same way that it is relative to the desired ellipse.
@@ -1686,16 +1673,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16861673 thetas = set ()
16871674 # For each of the point pairs, there is a line segment
16881675 for p0 , p1 in zip (box_path .vertices [:- 1 ], box_path .vertices [1 :]):
1689- x0 , y0 = p0
1690- x1 , y1 = p1
1691- for x , y in iter_circle_intersect_on_line_seg (x0 , y0 , x1 , y1 ):
1692- theta = np .arccos (x )
1693- if y < 0 :
1694- theta = 2 * np .pi - theta
1695- # Convert radians to angles
1696- theta = np .rad2deg (theta )
1697- if theta1 < theta < theta2 :
1698- thetas .add (theta )
1676+ xy = segment_circle_intersect (* p0 , * p1 )
1677+ x , y = xy .T
1678+ theta = np .rad2deg (np .arctan2 (y , x ))
1679+ thetas .update (theta [(theta1 < theta ) & (theta < theta2 )])
16991680 thetas = sorted (thetas ) + [theta2 ]
17001681
17011682 last_theta = theta1
0 commit comments