@@ -1569,9 +1569,191 @@ def get_angle(self):
1569
1569
angle = property (get_angle , set_angle )
1570
1570
1571
1571
1572
- class Circle (Ellipse ):
1573
- """A circle patch."""
1572
+ class Annulus (Patch ):
1573
+ """
1574
+ An elliptical annulus.
1575
+ """
1576
+
1577
+ @docstring .dedent_interpd
1578
+ def __init__ (self , xy , r , width , angle = 0.0 , ** kwargs ):
1579
+ """
1580
+ xy : (float, float)
1581
+ xy coordinates of annulus centre.
1582
+ r : float or (float, float)
1583
+ The radius, or semi-axes.
1584
+ - If float: radius of the outer circle.
1585
+ - If two floats: semi-major and -minor axes of outer ellipse.
1586
+ width : float
1587
+ Width (thickness) of the annular ring. The width is measured inward
1588
+ from the outer ellipse so that for the inner ellipse the semi-axes
1589
+ are given by `r - width`. `width` must be less than or equal to the
1590
+ semi-minor axis.
1591
+ angle : float, default=0
1592
+ Rotation angle in degrees (anti-clockwise from the positive
1593
+ x-axis). Ignored for circular annuli (ie. if *r* is a scalar).
1594
+
1595
+ Valid kwargs are:
1596
+
1597
+ %(Patch_kwdoc)s
1598
+ """
1599
+ super ().__init__ (** kwargs )
1600
+
1601
+ self .set_radii (r )
1602
+ self .center = xy
1603
+ self .width = width
1604
+ self .angle = angle
1605
+ self ._path = None
1606
+
1607
+ def __str__ (self ):
1608
+ if self .a == self .b :
1609
+ r = self .a
1610
+ else :
1611
+ r = (self .a , self .b )
1612
+
1613
+ return "Annulus(xy=(%s, %s), r=%s, width=%s, angle=%s)" % \
1614
+ (* self .center , r , self .width , self .angle )
1615
+
1616
+ def set_center (self , xy ):
1617
+ """
1618
+ Set the center of the annulus.
1619
+
1620
+ Parameters
1621
+ ----------
1622
+ xy : (float, float)
1623
+ """
1624
+ self ._center = xy
1625
+ self ._path = None
1626
+ self .stale = True
1627
+
1628
+ def get_center (self ):
1629
+ """Return the center of the annulus."""
1630
+ return self ._center
1631
+
1632
+ center = property (get_center , set_center )
1633
+
1634
+ def set_width (self , width ):
1635
+ """
1636
+ Set the width (thickness) of the annulus ring. The width is measured
1637
+ inwards from the outer ellipse.
1638
+
1639
+ Parameters
1640
+ ----------
1641
+ width : float
1642
+ """
1643
+ if min (self .a , self .b ) <= width :
1644
+ raise ValueError (
1645
+ 'Width of annulus must be less than or equal semi-minor axis' )
1646
+
1647
+ self ._width = width
1648
+ self ._path = None
1649
+ self .stale = True
1650
+
1651
+ def get_width (self ):
1652
+ """
1653
+ Return the width (thickness) of the annulus ring.
1654
+ """
1655
+ return self ._width
1656
+
1657
+ width = property (get_width , set_width )
1658
+
1659
+ def set_angle (self , angle ):
1660
+ """
1661
+ Set the tilt angle of the annulus.
1662
+
1663
+ Parameters
1664
+ ----------
1665
+ angle : float
1666
+ """
1667
+ self ._angle = angle
1668
+ self ._path = None
1669
+ self .stale = True
1670
+
1671
+ def get_angle (self ):
1672
+ """Return the angle of the annulus."""
1673
+ return self ._angle
1674
+
1675
+ angle = property (get_angle , set_angle )
1574
1676
1677
+ def set_semimajor (self , a ):
1678
+ """
1679
+ Set the semi-major axis *a* of the annulus.
1680
+
1681
+ Parameters
1682
+ ----------
1683
+ a : float
1684
+ """
1685
+ self .a = float (a )
1686
+ self ._path = None
1687
+ self .stale = True
1688
+
1689
+ def set_semiminor (self , b ):
1690
+ """
1691
+ Set the semi-minor axis *b* of the annulus.
1692
+
1693
+ Parameters
1694
+ ----------
1695
+ b : float
1696
+ """
1697
+ self .b = float (b )
1698
+ self ._path = None
1699
+ self .stale = True
1700
+
1701
+ def set_radii (self , r ):
1702
+ """
1703
+ Set the both the semi-major (*a*) and -minor radii (*b*) of the
1704
+ annulus.
1705
+
1706
+ Parameters
1707
+ ----------
1708
+ r : (float, float)
1709
+ """
1710
+ if np .shape (r ) == (2 ,):
1711
+ self .a , self .b = r
1712
+ elif np .shape (r ) == ():
1713
+ self .a = self .b = float (r )
1714
+ else :
1715
+ raise ValueError ("Parameter 'r' must be one or two floats." )
1716
+
1717
+ self ._path = None
1718
+ self .stale = True
1719
+
1720
+ def get_radii (self ):
1721
+ return self .a , self .b
1722
+
1723
+ radii = property (get_radii , set_radii )
1724
+
1725
+ def _transform_verts (self , verts , a , b ):
1726
+ return transforms .Affine2D () \
1727
+ .scale (* self ._convert_xy_units ((a , b ))) \
1728
+ .rotate_deg (self .angle ) \
1729
+ .translate (* self ._convert_xy_units (self .center )) \
1730
+ .transform (verts )
1731
+
1732
+ def _recompute_path (self ):
1733
+ # circular arc
1734
+ arc = Path .arc (0 , 360 )
1735
+
1736
+ # annulus needs to draw an outer ring
1737
+ # followed by a reversed and scaled inner ring
1738
+ a , b , w = self .a , self .b , self .width
1739
+ v1 = self ._transform_verts (arc .vertices , a , b )
1740
+ v2 = self ._transform_verts (arc .vertices [::- 1 ], a - w , b - w )
1741
+ v = np .vstack ([v1 , v2 , v1 [0 , :], (0 , 0 )])
1742
+ c = np .hstack ([arc .codes , Path .MOVETO ,
1743
+ arc .codes [1 :], Path .MOVETO ,
1744
+ Path .CLOSEPOLY ])
1745
+ self ._path = Path (v , c )
1746
+
1747
+ def get_path (self ):
1748
+ if self ._path is None :
1749
+ self ._recompute_path ()
1750
+ return self ._path
1751
+
1752
+
1753
+ class Circle (Ellipse ):
1754
+ """
1755
+ A circle patch.
1756
+ """
1575
1757
def __str__ (self ):
1576
1758
pars = self .center [0 ], self .center [1 ], self .radius
1577
1759
fmt = "Circle(xy=(%g, %g), radius=%g)"
0 commit comments