Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Improve RectangleSelector rotation #21945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions doc/users/next_whats_new/selector_improvement.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ Selectors improvement: rotation, aspect ratio correction and add/remove state
-----------------------------------------------------------------------------

The `~matplotlib.widgets.RectangleSelector` and
`~matplotlib.widgets.EllipseSelector` can now be rotated interactively between
-45° and 45°. The range limits are currently dictated by the implementation.
`~matplotlib.widgets.EllipseSelector` can now be rotated.
The rotation is enabled or disabled by striking the *r* key
('r' is the default key mapped to 'rotate' in *state_modifier_keys*) or by calling
``selector.add_state('rotate')``.
Expand Down
44 changes: 22 additions & 22 deletions lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,13 +738,6 @@ def __init__(self, xy, width, height, angle=0.0, *,
self._height = height
self.angle = float(angle)
self.rotation_point = rotation_point
# Required for RectangleSelector with axes aspect ratio != 1
# The patch is defined in data coordinates and when changing the
# selector with square modifier and not in data coordinates, we need
# to correct for the aspect ratio difference between the data and
# display coordinate systems. Its value is typically provide by
# Axes._get_aspect_ratio()
self._aspect_ratio_correction = 1.0
self._convert_units() # Validate the inputs.

def get_path(self):
Expand Down Expand Up @@ -772,13 +765,11 @@ def get_patch_transform(self):
rotation_point = bbox.x0, bbox.y0
else:
rotation_point = self.rotation_point
return transforms.BboxTransformTo(bbox) \
+ transforms.Affine2D() \
.translate(-rotation_point[0], -rotation_point[1]) \
.scale(1, self._aspect_ratio_correction) \
.rotate_deg(self.angle) \
.scale(1, 1 / self._aspect_ratio_correction) \
.translate(*rotation_point)
return (transforms.BboxTransformTo(bbox) +
transforms.Affine2D()
.translate(-rotation_point[0], -rotation_point[1])
.rotate_deg(self.angle)
.translate(*rotation_point))

@property
def rotation_point(self):
Expand Down Expand Up @@ -816,6 +807,14 @@ def get_corners(self):
return self.get_patch_transform().transform(
[(0, 0), (1, 0), (1, 1), (0, 1)])

def _get_edge_midpoints(self):
"""
Return the edge midpoints of the rectangle, moving anti-clockwise from
the center of the left-hand edge.
"""
return self.get_patch_transform().transform(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is private API, and is implicitly tested by a lot of the selector tests. If the points were somehow wrong here, the handles would be in the wrong place, and if they were correct but in the wrong order the resizing logic would not work as intended.

[(0, 0.5), (0.5, 0), (1, 0.5), (0.5, 1)])

def get_center(self):
"""Return the centre of the rectangle."""
return self.get_patch_transform().transform((0.5, 0.5))
Expand Down Expand Up @@ -1565,12 +1564,6 @@ def __init__(self, xy, width, height, angle=0, **kwargs):
self._width, self._height = width, height
self._angle = angle
self._path = Path.unit_circle()
# Required for EllipseSelector with axes aspect ratio != 1
# The patch is defined in data coordinates and when changing the
# selector with square modifier and not in data coordinates, we need
# to correct for the aspect ratio difference between the data and
# display coordinate systems.
self._aspect_ratio_correction = 1.0
# Note: This cannot be calculated until this is added to an Axes
self._patch_transform = transforms.IdentityTransform()

Expand All @@ -1588,9 +1581,8 @@ def _recompute_transform(self):
width = self.convert_xunits(self._width)
height = self.convert_yunits(self._height)
self._patch_transform = transforms.Affine2D() \
.scale(width * 0.5, height * 0.5 * self._aspect_ratio_correction) \
.scale(width * 0.5, height * 0.5) \
.rotate_deg(self.angle) \
.scale(1, 1 / self._aspect_ratio_correction) \
.translate(*center)

def get_path(self):
Expand Down Expand Up @@ -1681,6 +1673,14 @@ def get_corners(self):
return self.get_patch_transform().transform(
[(-1, -1), (1, -1), (1, 1), (-1, 1)])

def _get_edge_midpoints(self):
"""
Return the corners of the ellipse, moving anti-clockwise from
the center of the left-hand edge before rotation.
"""
return self.get_patch_transform().transform(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

[(-1, 0), (0, -1), (1, 0), (0, 1)])


class Annulus(Patch):
"""
Expand Down
Loading