matplotlibでtransformを指定したpatchをadd_patchすると親axesの座標系を無視する

matplotlibでpatchにtransformをつけると消えてしまうように見える現象が起きた。
まず、散布図の上に普通に長方形のpatchを乗せてみる。

clf()
scatter(random(100), random(100))
ax = gca()
rect = Rectangle((0, 0), 1, 1, alpha=0.3)
ax.add_patch(rect)
matplotlib.pyplot.savefig("test.png", dpi=100)

ここまではとても自然にできる。じゃあこの長方形を回転してみよう、とtransformを指定すると消えてしまう。transformの作り方がまずいのかと思ったがAffine2D.identity()でも消えてしまう。

clf()
scatter(random(100), random(100))
ax = gca()
t = Affine2D.identity()
rect = Rectangle((0, 0), 1, 1, transform=t, alpha=0.3)
ax.add_patch(rect)
matplotlib.pyplot.savefig("test.png", dpi=100)

問題解決の鍵は前回(matplotlibのPatchCollectionは子パッチの色を上書きする)同様、patch自体ではなくその上にあった。

Definition: ax.add_patch(self, p)
Docstring:
Add a :class:`~matplotlib.patches.Patch` *p* to the list of
axes patches; the clipbox will be set to the Axes clipping
box.  If the transform is not set, it will be set to
:attr:`transData`.

つまりpatchにtransformを指定してadd_patchすると親axesの座標系を無視してしまう。っていうわけで今回の例で言えばtransformを指定しないときには散布図の軸に従った座標系で0〜1の範囲にあったものが、単位行列をtransformに指定したことで図のグローバル座標系になってしまっているわけだ。そして散布図のクリッピングの外なので消えてしまっている。長方形のサイズを100x100にしてみるとちょこっと頭を出す:「やあ、僕はここだよ!」

rect = Rectangle((0, 0), 100, 100, transform=t, alpha=0.3)

じゃあax.transDataをtransformに指定すればいいわけね。

t = ax.transData
rect = Rectangle((0, 0), 1, 1, transform=t, alpha=0.3)

コレは期待通りの結果だったので省略。じゃあ早速回転してみよう。

for i in range(5):
    t = CompositeGenericTransform(Affine2D.identity().rotate(0.1 * i), ax.transData)
    rect = Rectangle((0, 0), 1, 1, transform=t, alpha=0.3)
    ax.add_patch(rect)

できたできた。めでたしめでたし。でも、本当にCompositeGenericTransformなんか使わないといけないのだろうか。もっと楽な方法があるんじゃないかなぁ。

追記。とりあえずidentityは使わなくてもAffine2D()で単位行列になるみたい。