@@ -706,7 +706,8 @@ def __str__(self):
706
706
return fmt % pars
707
707
708
708
@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 ):
710
711
"""
711
712
Parameters
712
713
----------
@@ -717,7 +718,11 @@ def __init__(self, xy, width, height, angle=0.0, **kwargs):
717
718
height : float
718
719
Rectangle height.
719
720
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.
721
726
722
727
Other Parameters
723
728
----------------
@@ -730,6 +735,14 @@ def __init__(self, xy, width, height, angle=0.0, **kwargs):
730
735
self ._width = width
731
736
self ._height = height
732
737
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
733
746
self ._convert_units () # Validate the inputs.
734
747
735
748
def get_path (self ):
@@ -750,9 +763,36 @@ def get_patch_transform(self):
750
763
# important to call the accessor method and not directly access the
751
764
# transformation member variable.
752
765
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)}." )
756
796
757
797
def get_x (self ):
758
798
"""Return the left coordinate of the rectangle."""
@@ -1511,6 +1551,12 @@ def __init__(self, xy, width, height, angle=0, **kwargs):
1511
1551
self ._width , self ._height = width , height
1512
1552
self ._angle = angle
1513
1553
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
1514
1560
# Note: This cannot be calculated until this is added to an Axes
1515
1561
self ._patch_transform = transforms .IdentityTransform ()
1516
1562
@@ -1528,8 +1574,9 @@ def _recompute_transform(self):
1528
1574
width = self .convert_xunits (self ._width )
1529
1575
height = self .convert_yunits (self ._height )
1530
1576
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 ) \
1532
1578
.rotate_deg (self .angle ) \
1579
+ .scale (1 , 1 / self ._aspect_ratio_correction ) \
1533
1580
.translate (* center )
1534
1581
1535
1582
def get_path (self ):
0 commit comments