@@ -1569,9 +1569,191 @@ def get_angle(self):
15691569 angle = property (get_angle , set_angle )
15701570
15711571
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 )
15741676
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+ """
15751757 def __str__ (self ):
15761758 pars = self .center [0 ], self .center [1 ], self .radius
15771759 fmt = "Circle(xy=(%g, %g), radius=%g)"
0 commit comments