matplotlib.patheffects
破線を使用してパスを描画するために使用しています。私の目標は、各パスをパスに沿って等間隔に正確に N 個のセグメントに分割することです。ダッシュはデータ座標ではなくピクセル長で指定する必要があるため、私が使用しax.transData.transform_point()
ているものはほとんどの状況で機能しますが、図を縮小する必要がある場合、ダッシュは適切にスケーリングされません.
問題を示すコード サンプルを次に示します。
import sys
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
from matplotlib.patches import PathPatch
from matplotlib.path import Path
import numpy as np
MIN=1 if len(sys.argv) <= 1 else int(sys.argv[1])
MAX=MIN if len(sys.argv) <=2 else int(sys.argv[2])
SCALE=4 if len(sys.argv) <=3 else int(sys.argv[3])
COUNT=MAX-MIN+1
fig = plt.figure(1, figsize=(COUNT*SCALE,SCALE))
for i in range(MIN, MAX+1):
ax = plt.subplot(1, COUNT, i-MIN+1)
ax.set_xlim(0, i)
ax.set_ylim(0, i)
path = Path([(0,0), (0,i) ,(i,i)], [Path.MOVETO, Path.CURVE3, Path.CURVE3])
pp = PathPatch(path, fill=False)
def path_length(path):
QI_SAMPLES=1000
v = path.vertices
v = [ax.transData.transform_point(p) for p in v ]
if len(v) > 2:
v = [ quad_interpolate( i/(1.0 * QI_SAMPLES), v[0], v[1], v[2])
for i in range(0, QI_SAMPLES)]
dv = np.diff(v, axis=0)
return np.sum(np.sqrt(np.sum(dv**2, axis=-1)))
def quad_interpolate(i, A, B, C):
(ax, ay) = A
(bx, by) = B
(cx, cy) = C
ii = 1.-i
i2 = i*i
ii2 = ii*ii
rx = ax*ii2 + 2*bx*ii*i + cx*i2
ry = ay*ii2 + 2*by*ii*i + cy*i2
return (rx, ry)
ax.add_patch(pp)
length = path_length(path)
dash_length = length/(2*i)
space_length = length/(2*i)
offset=-(space_length/2)
pe = [PathEffects.withStroke(linewidth=3,
dashes=[offset, (dash_length, space_length)],
foreground="b")]
pp.set_path_effects(pe)
plt.show()
コマンド ライン引数は (最小セグメント) (最大セグメント) (サブプロット スケール) です。ディスプレイのサイズによっては、問題を引き起こすために別のコマンドライン引数を指定して実行する必要があるかもしれません。私のシステムでは、「1 8 3」で実行すると次のようになります。
「1 8 4」で実行すると、次のようになります。
ご覧のとおり、2 番目のプロットはまったく正しくありません。8 番目のサブプロットにはダッシュが 7 つしかなく、他のプロットも少しずれています。
スケールに関係なく、ダッシュの長さが基になる軸の座標空間に確実に変換されるようにするにはどうすればよいですか? または、破線をピクセル長で指定することに依存しないパスに沿って破線を取得する他の方法はありますか?
更新: 曲線の代わりに線をプロットすると、次のようになります。良い:
悪い:
コメントで指摘されているように、サブプロットが完全に正方形ではないため、「良い」プロットでさえわずかにずれているようです。しかし、ax.transData.transform_point
変換はこれを処理することになっていませんか?