matplotlibでペンローズの三角形を描く方法
ペンローズの三角形は、不可能図形として知られる有名な幾何学的図形です。この記事では、Pythonのmatplotlibライブラリを使用して、ペンローズの三角形を描画する方法を解説します。
解説
必要なライブラリ
まず、必要なライブラリをインポートします。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
この例では、matplotlibとnumpyを使用します。Polygonクラスを使って多角形を描画します。
基本的な構造
ペンローズの三角形は3つの面から構成されており、それぞれが異なる色で塗られています。各面は多角形として定義され、Polygonパッチを使って描画します。
図の初期化
fig, ax = plt.subplots(dpi=100)
ax.set_aspect('equal')
subplots()で図とaxesオブジェクトを作成し、set_aspect('equal')で縦横比を等しく設定します。これにより、図形が正しい形で表示されます。
第1の面(サーモンピンク)
最初の面を描画します。
p1 = np.array([(0,68), (69, 68), (64, 60), (14, 60), (48,0), (38,0), (0,68)])
polygon1 = Polygon(p1, closed=True, edgecolor="k", facecolor='salmon', linewidth=1)
ax.add_patch(polygon1)
np.array()で頂点の座標を定義し、Polygonオブジェクトを作成します。closed=Trueで多角形を閉じ、edgecolorとfacecolorで枠線と塗りつぶしの色を指定します。
第2の面(スプリンググリーン)
2つ目の面を描画します。
p2 = np.array([(0,68), (4, 76), (84, 76), (48, 17), (43.5,23.5), (69,68), (0,68)])
polygon2 = Polygon(p2, closed=True, edgecolor="k", facecolor='springgreen', linewidth=1)
ax.add_patch(polygon2)
同様の方法で、異なる頂点座標と色を使って2つ目の面を作成します。
第3の面(カーキ)
最後の面を描画します。
p3 = np.array([(84,76), (88, 68), (48, 0), (14, 60), (23,60), (48,17), (84,76)])
polygon3 = Polygon(p3, closed=True, edgecolor="k", facecolor='khaki', linewidth=1)
ax.add_patch(polygon3)
3つ目の面も同じ手順で作成します。
図の表示
plt.show()
plt.show()で図を表示します。
完成コード
すべてをまとめた完成コードは以下の通りです。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
patches = []
fig, ax = plt.subplots(dpi=100)
p1=np.array([(0,68), (69, 68), (64, 60), (14, 60),(48,0),(38,0),(0,68)])
polygon1 = Polygon(p1,closed=True, edgecolor="k",facecolor='salmon', linewidth=1)
ax.plot(p1[:,0],p1[:,1],alpha=0)
ax.add_patch(polygon1)
p2=np.array([(0,68), (4, 76), (84, 76), (48, 17),(43.5,23.5),(69,68),(0,68)])
polygon2 = Polygon(p2,closed=True, edgecolor="k",facecolor='springgreen', linewidth=1)
ax.plot(p2[:,0],p2[:,1],alpha=0)
ax.add_patch(polygon2)
p3=np.array([(84,76), (88, 68), (48, 0), (14, 60),(23,60),(48,17),(84,76)])
polygon3 = Polygon(p3,closed=True, edgecolor="k",facecolor='khaki', linewidth=1)
ax.plot(p3[:,0],p3[:,1],alpha=0)
ax.add_patch(polygon3)
ax.set_aspect('equal')
plt.savefig("penrose.png",dpi=100)
plt.show()

ax.plotの役割について
上記のコードでは、各多角形を描画した後にax.plot(p2[:,0],p2[:,1],alpha=0)のように透明なプロットを作成しています。これは、matplotlibのオートスケール機能を有効にするための工夫です。
Polygonパッチだけを追加した場合、matplotlibは自動的に軸の範囲を調整しません。そのため、透明度を0に設定した(alpha=0)プロットを追加することで、座標データを軸に認識させ、適切な表示範囲が自動的に設定されるようにしています。
つまり、この透明なプロットは視覚的には表示されませんが、オートスケールを機能させるために必要な処理となります。
まとめ
この記事では、matplotlibを使ってペンローズの三角形を描く方法を解説しました。Polygonパッチを使うことで、複雑な幾何学的図形も簡単に描画できます。
おまけ:色をランダムに変えたペンローズの三角形
以下のコードでは、各面の色をランダムに生成してペンローズの三角形を描画します。
import matplotlib.pyplot as plt
import numpy as np
import random
from matplotlib.patches import Polygon
# ランダムな色を生成する関数
def random_color():
return (random.random(), random.random(), random.random())
fig, ax = plt.subplots(dpi=100)
# 第1の面
p1 = np.array([(0,68), (69, 68), (64, 60), (14, 60), (48,0), (38,0), (0,68)])
polygon1 = Polygon(p1, closed=True, edgecolor="k", facecolor=random_color(), linewidth=1)
ax.plot(p1[:,0], p1[:,1], alpha=0)
ax.add_patch(polygon1)
# 第2の面
p2 = np.array([(0,68), (4, 76), (84, 76), (48, 17), (43.5,23.5), (69,68), (0,68)])
polygon2 = Polygon(p2, closed=True, edgecolor="k", facecolor=random_color(), linewidth=1)
ax.plot(p2[:,0], p2[:,1], alpha=0)
ax.add_patch(polygon2)
# 第3の面
p3 = np.array([(84,76), (88, 68), (48, 0), (14, 60), (23,60), (48,17), (84,76)])
polygon3 = Polygon(p3, closed=True, edgecolor="k", facecolor=random_color(), linewidth=1)
ax.plot(p3[:,0], p3[:,1], alpha=0)
ax.add_patch(polygon3)
ax.set_aspect('equal')
plt.savefig("penrose_cc.png",dpi=100)
plt.show()

コードの説明
random_color()関数: この関数は、0から1の範囲でランダムなRGB値を生成し、タプルとして返します。matplotlibでは、RGB値を(R, G, B)の形式で指定でき、各値は0から1の範囲で指定します。
facecolor=random_color(): 各Polygonオブジェクトのfacecolorパラメータにrandom_color()関数を使用することで、実行するたびに異なる色の組み合わせでペンローズの三角形が描画されます。
実行結果
このコードを実行すると、3つの面がそれぞれランダムな色で塗られたペンローズの三角形が表示されます。プログラムを実行するたびに異なる色の組み合わせが生成されるため、様々なバリエーションを楽しむことができます。
おまけ2:SVGファイルで書き出してベクターグラフィックソフトで自由に加工
matplotlibで作成した図は、SVG(Scalable Vector Graphics)形式で保存することができます。SVGはベクター形式なので、拡大しても画質が劣化せず、InkscapeやAdobe Illustratorなどのベクターグラフィックソフトで自由に編集・加工が可能です。
SVGファイルとして保存する方法
以下のように、plt.savefig()を使ってSVG形式で保存できます。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
fig, ax = plt.subplots(dpi=100)
p1 = np.array([(0,68), (69, 68), (64, 60), (14, 60), (48,0), (38,0), (0,68)])
polygon1 = Polygon(p1, closed=True, edgecolor="k", facecolor='salmon', linewidth=1)
ax.plot(p1[:,0], p1[:,1], alpha=0)
ax.add_patch(polygon1)
p2 = np.array([(0,68), (4, 76), (84, 76), (48, 17), (43.5,23.5), (69,68), (0,68)])
polygon2 = Polygon(p2, closed=True, edgecolor="k", facecolor='springgreen', linewidth=1)
ax.plot(p2[:,0], p2[:,1], alpha=0)
ax.add_patch(polygon2)
p3 = np.array([(84,76), (88, 68), (48, 0), (14, 60), (23,60), (48,17), (84,76)])
polygon3 = Polygon(p3, closed=True, edgecolor="k", facecolor='khaki', linewidth=1)
ax.plot(p3[:,0], p3[:,1], alpha=0)
ax.add_patch(polygon3)
ax.set_aspect('equal')
ax.axis('off') # 軸を非表示にする
# SVG形式で保存
plt.savefig('penrose_triangle.svg', format='svg', bbox_inches='tight')
plt.show()
plt.savefig('penrose_triangle.svg', format='svg', bbox_inches='tight')により、カレントディレクトリに「penrose_triangle.svg」というファイルが保存されます。bbox_inches='tight'は余白を最小限にするオプションです。
Inkscapeでの編集
保存したSVGファイルをInkscapeで開くと、各多角形が個別のオブジェクトとして認識されます。これにより、以下のような編集が可能になります。
- 各面の色を個別に変更
- グラデーションやパターンの適用
- 影やぼかし効果の追加
- 線の太さやスタイルの調整
- 他のグラフィック要素との組み合わせ
このように、プログラムで生成した図形をベクターグラフィックソフトで更に洗練させることができます。

noteマガジンのご案内
Inkscapeのノウハウについて、noteで定期的に記事を公開しています。より詳しい解説や応用例、実践的なテクニックについて知りたい方は、ぜひnoteマガジンをご購読ください。




参考資料
この記事の作成にあたり、以下のWebページを参考にしました。
- https://matplotlib.org/Matplotlib公式ドキュメント – Pythonでの可視化ライブラリの総合的なガイド
- https://en.wikipedia.org/wiki/Penrose_triangleペンローズの三角形 – Wikipedia – 不可能図形の理論と歴史
- https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.htmlPolygonクラスのドキュメント – 多角形描画の詳細な仕様
- https://numpy.org/doc/stable/NumPy公式ドキュメント – 数値計算ライブラリの使用方法

コメント