Skip to content
This repository

Add angle kwarg to patches.Rectangle #1405

Merged
merged 5 commits into from over 1 year ago

4 participants

Damon McDougall Wolfgang Kerzendorf Phil Elson Benjamin Root
Damon McDougall
Collaborator

Allows rotation of a rectangle upon instantiation.

Addresses #987.
The old attempt was #1156.

Damon McDougall
Collaborator

Here's an example:

import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
r = Rectangle((0.5, 1.0), 2, 1, angle=45.0)
ax.add_patch(r)
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
ax.set_aspect('equal')
fig.savefig('rect.pdf')
Phil Elson
Collaborator

Nice solution to the problem. :+1:

Wolfgang Kerzendorf

When will this be merged? It's really useful to me.

Damon McDougall
Collaborator

I should add an entry to api_changes or whats_new (one of the other devs should advise me on this). It may be the case we are able to extend this for other artists, too. That should be done in a different pull request, though. This PR was only meant to satisfy the feature request in #987.

Benjamin Root
Collaborator
Damon McDougall
Collaborator

Added a note in whats_new and added a test, too. Go me. When my friend Travis goes green, I consider this ready to merge. Others should review the code for any heinous style issues or hidden treasures.

doc/users/whats_new.rst
... ...
@@ -20,6 +20,11 @@ revision, see the :ref:`github-stats`.
20 20
 new in matplotlib-1.3
21 21
 =====================
22 22
 
  23
+Initialize a rotated rectangle
  24
+------------------------------
  25
+Damon McDougall extended the `~matplotlib.patches.Rectangle` constructor to
2
Benjamin Root Collaborator

should be :class:`~matplotlib.patches.Rectangle`

Damon McDougall Collaborator

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/matplotlib/patches.py
... ...
@@ -574,7 +578,10 @@ def _update_patch_transform(self):
574 578
         width = self.convert_xunits(self._width)
575 579
         height = self.convert_yunits(self._height)
576 580
         bbox = transforms.Bbox.from_bounds(x, y, width, height)
  581
+        rot_trans = transforms.Affine2D()
  582
+        rot_trans.translate(-x, -y).rotate_deg(self._angle).translate(x, y)
577 583
         self._rect_transform = transforms.BboxTransformTo(bbox)
2
Benjamin Root Collaborator

Why the translation?

Damon McDougall Collaborator

Because I didn't know rotate_deg_around existed. I will push an update to use that instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Wolfgang Kerzendorf

@dmcdougall How's your friend Travis going?

Damon McDougall
Collaborator

@wkerzendorf He gave this PR the all-clear. I'm just waiting on some of the other devs to give feedback and merge it. I am against merging my own pull requests unless they're trivial changes.

Phil Elson pelson merged commit 17e1cf8 into from November 13, 2012
Phil Elson pelson closed this November 13, 2012
Phil Elson
Collaborator

Thanks @dmcdougall (and @wkerzendorf for the nudging :wink: ). Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
5  doc/users/whats_new.rst
Source Rendered
@@ -20,6 +20,11 @@ revision, see the :ref:`github-stats`.
20 20
 new in matplotlib-1.3
21 21
 =====================
22 22
 
  23
+Initialize a rotated rectangle
  24
+------------------------------
  25
+Damon McDougall extended the :class:`~matplotlib.patches.Rectangle` constructor
  26
+to accept an `angle` kwarg, specifying the rotation of a rectangle in degrees.
  27
+
23 28
 Rectangular colorbar extensions
24 29
 -------------------------------
25 30
 Andrew Dawson added a new keyword argument *extendrect* to
9  lib/matplotlib/patches.py
@@ -539,9 +539,12 @@ def __str__(self):
539 539
             + "(%g,%g;%gx%g)" % (self._x, self._y, self._width, self._height)
540 540
 
541 541
     @docstring.dedent_interpd
542  
-    def __init__(self, xy, width, height, **kwargs):
  542
+    def __init__(self, xy, width, height, angle=0.0, **kwargs):
543 543
         """
544 544
 
  545
+        *angle*
  546
+          rotation in degrees (anti-clockwise)
  547
+
545 548
         *fill* is a boolean indicating whether to fill the rectangle
546 549
 
547 550
         Valid kwargs are:
@@ -554,6 +557,7 @@ def __init__(self, xy, width, height, **kwargs):
554 557
         self._y = xy[1]
555 558
         self._width = width
556 559
         self._height = height
  560
+        self._angle = angle
557 561
         # Note: This cannot be calculated until this is added to an Axes
558 562
         self._rect_transform = transforms.IdentityTransform()
559 563
 
@@ -574,7 +578,10 @@ def _update_patch_transform(self):
574 578
         width = self.convert_xunits(self._width)
575 579
         height = self.convert_yunits(self._height)
576 580
         bbox = transforms.Bbox.from_bounds(x, y, width, height)
  581
+        rot_trans = transforms.Affine2D()
  582
+        rot_trans.rotate_deg_around(x, y, self._angle)
577 583
         self._rect_transform = transforms.BboxTransformTo(bbox)
  584
+        self._rect_transform += rot_trans
578 585
 
579 586
     def get_patch_transform(self):
580 587
         self._update_patch_transform()
25  lib/matplotlib/tests/test_patches.py
@@ -2,8 +2,11 @@
2 2
 Tests specific to the patches module.
3 3
 """
4 4
 
  5
+import numpy as np
5 6
 from numpy.testing import assert_array_equal
  7
+from numpy.testing import assert_almost_equal
6 8
 from matplotlib.patches import Polygon
  9
+from matplotlib.patches import Rectangle
7 10
 
8 11
 def test_Polygon_close():
9 12
     """
@@ -40,3 +43,25 @@ def test_Polygon_close():
40 43
     p.set_xy(xyclosed)
41 44
     assert_array_equal(p.get_xy(), xyclosed)
42 45
 
  46
+def test_rotate_rect():
  47
+    loc = np.asarray([1.0, 2.0])
  48
+    width = 2
  49
+    height = 3
  50
+    angle = 30.0
  51
+
  52
+    # A rotated rectangle
  53
+    rect1 = Rectangle(loc, width, height, angle=angle)
  54
+
  55
+    # A non-rotated rectangle
  56
+    rect2 = Rectangle(loc, width, height)
  57
+
  58
+    # Set up an explicit rotation matrix (in radians)
  59
+    angle_rad = np.pi * angle / 180.0
  60
+    rotation_matrix = np.array([[np.cos(angle_rad), -np.sin(angle_rad)],
  61
+                                [np.sin(angle_rad),  np.cos(angle_rad)]])
  62
+
  63
+    # Translate to origin, rotate each vertex, and then translate back
  64
+    new_verts = np.inner(rotation_matrix, rect2.get_verts() - loc).T + loc
  65
+
  66
+    # They should be the same
  67
+    assert_almost_equal(rect1.get_verts(), new_verts)
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.