@@ -706,7 +706,8 @@ def __str__(self):
706706 return fmt % pars
707707
708708 @docstring .dedent_interpd
709- def __init__ (self , xy , width , height , angle = 0.0 , ** kwargs ):
709+ def __init__ (self , xy , width , height , angle = 0.0 , * ,
710+ rotation_point = 'xy' , ** kwargs ):
710711 """
711712 Parameters
712713 ----------
@@ -717,7 +718,11 @@ def __init__(self, xy, width, height, angle=0.0, **kwargs):
717718 height : float
718719 Rectangle height.
719720 angle : float, default: 0
720- Rotation in degrees anti-clockwise about *xy*.
721+ Rotation in degrees anti-clockwise about the rotation point.
722+ rotation_point : {'xy', 'center', (number, number)}, default: 'xy'
723+ If ``'xy'``, rotate around the anchor point. If ``'center'`` rotate
724+ around the center. If 2-tuple of number, rotate around this
725+ coordinate.
721726
722727 Other Parameters
723728 ----------------
@@ -730,6 +735,14 @@ def __init__(self, xy, width, height, angle=0.0, **kwargs):
730735 self ._width = width
731736 self ._height = height
732737 self .angle = float (angle )
738+ self .rotation_point = rotation_point
739+ # Required for RectangleSelector with axes aspect ratio != 1
740+ # The patch is defined in data coordinates and when changing the
741+ # selector with square modifier and not in data coordinates, we need
742+ # to correct for the aspect ratio difference between the data and
743+ # display coordinate systems. Its value is typically provide by
744+ # Axes._get_aspect_ratio()
745+ self ._aspect_ratio_correction = 1.0
733746 self ._convert_units () # Validate the inputs.
734747
735748 def get_path (self ):
@@ -750,9 +763,36 @@ def get_patch_transform(self):
750763 # important to call the accessor method and not directly access the
751764 # transformation member variable.
752765 bbox = self .get_bbox ()
753- return (transforms .BboxTransformTo (bbox )
754- + transforms .Affine2D ().rotate_deg_around (
755- bbox .x0 , bbox .y0 , self .angle ))
766+ if self .rotation_point == 'center' :
767+ width , height = bbox .x1 - bbox .x0 , bbox .y1 - bbox .y0
768+ rotation_point = bbox .x0 + width / 2. , bbox .y0 + height / 2.
769+ elif self .rotation_point == 'xy' :
770+ rotation_point = bbox .x0 , bbox .y0
771+ else :
772+ rotation_point = self .rotation_point
773+ return transforms .BboxTransformTo (bbox ) \
774+ + transforms .Affine2D () \
775+ .translate (- rotation_point [0 ], - rotation_point [1 ]) \
776+ .scale (1 , self ._aspect_ratio_correction ) \
777+ .rotate_deg (self .angle ) \
778+ .scale (1 , 1 / self ._aspect_ratio_correction ) \
779+ .translate (* rotation_point )
780+
781+ @property
782+ def rotation_point (self ):
783+ """The rotation point of the patch."""
784+ return self ._rotation_point
785+
786+ @rotation_point .setter
787+ def rotation_point (self , value ):
788+ if value in ['center' , 'xy' ] or (
789+ isinstance (value , tuple ) and len (value ) == 2 and
790+ isinstance (value [0 ], Number ) and isinstance (value [1 ], Number )
791+ ):
792+ self ._rotation_point = value
793+ else :
794+ raise ValueError ("`rotation_point` must be one of "
795+ "{'xy', 'center', (number, number)}." )
756796
757797 def get_x (self ):
758798 """Return the left coordinate of the rectangle."""
@@ -1511,6 +1551,12 @@ def __init__(self, xy, width, height, angle=0, **kwargs):
15111551 self ._width , self ._height = width , height
15121552 self ._angle = angle
15131553 self ._path = Path .unit_circle ()
1554+ # Required for EllipseSelector with axes aspect ratio != 1
1555+ # The patch is defined in data coordinates and when changing the
1556+ # selector with square modifier and not in data coordinates, we need
1557+ # to correct for the aspect ratio difference between the data and
1558+ # display coordinate systems.
1559+ self ._aspect_ratio_correction = 1.0
15141560 # Note: This cannot be calculated until this is added to an Axes
15151561 self ._patch_transform = transforms .IdentityTransform ()
15161562
@@ -1528,8 +1574,9 @@ def _recompute_transform(self):
15281574 width = self .convert_xunits (self ._width )
15291575 height = self .convert_yunits (self ._height )
15301576 self ._patch_transform = transforms .Affine2D () \
1531- .scale (width * 0.5 , height * 0.5 ) \
1577+ .scale (width * 0.5 , height * 0.5 * self . _aspect_ratio_correction ) \
15321578 .rotate_deg (self .angle ) \
1579+ .scale (1 , 1 / self ._aspect_ratio_correction ) \
15331580 .translate (* center )
15341581
15351582 def get_path (self ):
0 commit comments