@@ -1592,14 +1592,12 @@ def draw(self, renderer):
1592
1592
calculation much easier than doing rotated ellipse
1593
1593
intersection directly).
1594
1594
1595
- This uses the "line intersecting a circle" algorithm
1596
- from:
1595
+ This uses the "line intersecting a circle" algorithm from:
1597
1596
1598
1597
Vince, John. *Geometry for Computer Graphics: Formulae,
1599
1598
Examples & Proofs.* London: Springer-Verlag, 2005.
1600
1599
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.
1603
1601
1604
1602
3. Proceeding counterclockwise starting in the positive
1605
1603
x-direction, each of the visible arc-segments between the
@@ -1632,33 +1630,25 @@ def theta_stretch(theta, scale):
1632
1630
self ._path = Path .arc (theta1 , theta2 )
1633
1631
return Patch .draw (self , renderer )
1634
1632
1635
- def iter_circle_intersect_on_line (x0 , y0 , x1 , y1 ):
1633
+ def line_circle_intersect (x0 , y0 , x1 , y1 ):
1636
1634
dx = x1 - x0
1637
1635
dy = y1 - y0
1638
1636
dr2 = dx * dx + dy * dy
1639
1637
D = x0 * y1 - x1 * y0
1640
1638
D2 = D * D
1641
1639
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.
1655
1642
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 ))
1660
1650
1661
- def iter_circle_intersect_on_line_seg (x0 , y0 , x1 , y1 ):
1651
+ def segment_circle_intersect (x0 , y0 , x1 , y1 ):
1662
1652
epsilon = 1e-9
1663
1653
if x1 < x0 :
1664
1654
x0e , x1e = x1 , x0
@@ -1668,13 +1658,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1668
1658
y0e , y1e = y1 , y0
1669
1659
else :
1670
1660
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 )]
1678
1665
1679
1666
# Transforms the axes box_path so that it is relative to the unit
1680
1667
# 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):
1686
1673
thetas = set ()
1687
1674
# For each of the point pairs, there is a line segment
1688
1675
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 )])
1699
1680
thetas = sorted (thetas ) + [theta2 ]
1700
1681
1701
1682
last_theta = theta1
0 commit comments